@tanstack/start-plugin-core 1.167.34 → 1.168.0

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.
Files changed (197) hide show
  1. package/dist/esm/import-protection/adapterUtils.d.ts +27 -0
  2. package/dist/esm/import-protection/adapterUtils.js +31 -0
  3. package/dist/esm/import-protection/adapterUtils.js.map +1 -0
  4. package/dist/esm/import-protection/analysis.d.ts +36 -0
  5. package/dist/esm/import-protection/analysis.js +407 -0
  6. package/dist/esm/import-protection/analysis.js.map +1 -0
  7. package/dist/esm/{import-protection-plugin → import-protection}/ast.js +1 -1
  8. package/dist/esm/import-protection/ast.js.map +1 -0
  9. package/dist/esm/import-protection/constants.d.ts +11 -0
  10. package/dist/esm/{import-protection-plugin → import-protection}/constants.js +7 -2
  11. package/dist/esm/import-protection/constants.js.map +1 -0
  12. package/dist/esm/{import-protection-plugin → import-protection}/defaults.js +1 -1
  13. package/dist/esm/import-protection/defaults.js.map +1 -0
  14. package/dist/esm/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.js +1 -1
  15. package/dist/esm/import-protection/extensionlessAbsoluteIdResolver.js.map +1 -0
  16. package/dist/esm/{import-protection-plugin → import-protection}/matchers.js +1 -1
  17. package/dist/esm/import-protection/matchers.js.map +1 -0
  18. package/dist/esm/{import-protection-plugin/rewriteDeniedImports.d.ts → import-protection/rewrite.d.ts} +0 -4
  19. package/dist/esm/import-protection/rewrite.js +121 -0
  20. package/dist/esm/import-protection/rewrite.js.map +1 -0
  21. package/dist/esm/{import-protection-plugin → import-protection}/sourceLocation.d.ts +32 -3
  22. package/dist/esm/{import-protection-plugin → import-protection}/sourceLocation.js +65 -10
  23. package/dist/esm/import-protection/sourceLocation.js.map +1 -0
  24. package/dist/esm/{import-protection-plugin → import-protection}/trace.d.ts +0 -1
  25. package/dist/esm/{import-protection-plugin → import-protection}/trace.js +1 -1
  26. package/dist/esm/import-protection/trace.js.map +1 -0
  27. package/dist/esm/{import-protection-plugin → import-protection}/utils.d.ts +18 -1
  28. package/dist/esm/{import-protection-plugin → import-protection}/utils.js +12 -19
  29. package/dist/esm/import-protection/utils.js.map +1 -0
  30. package/dist/esm/import-protection/virtualModules.d.ts +25 -0
  31. package/dist/esm/{import-protection-plugin → import-protection}/virtualModules.js +5 -117
  32. package/dist/esm/import-protection/virtualModules.js.map +1 -0
  33. package/dist/esm/index.d.ts +4 -0
  34. package/dist/esm/index.js +3 -1
  35. package/dist/esm/post-build.d.ts +9 -0
  36. package/dist/esm/post-build.js +37 -0
  37. package/dist/esm/post-build.js.map +1 -0
  38. package/dist/esm/prerender.d.ts +11 -0
  39. package/dist/esm/prerender.js +159 -0
  40. package/dist/esm/prerender.js.map +1 -0
  41. package/dist/esm/rsbuild/dev-server.d.ts +21 -0
  42. package/dist/esm/rsbuild/dev-server.js +76 -0
  43. package/dist/esm/rsbuild/dev-server.js.map +1 -0
  44. package/dist/esm/rsbuild/import-protection.d.ts +10 -0
  45. package/dist/esm/rsbuild/import-protection.js +775 -0
  46. package/dist/esm/rsbuild/import-protection.js.map +1 -0
  47. package/dist/esm/rsbuild/normalized-client-build.d.ts +18 -0
  48. package/dist/esm/rsbuild/normalized-client-build.js +207 -0
  49. package/dist/esm/rsbuild/normalized-client-build.js.map +1 -0
  50. package/dist/esm/rsbuild/planning.d.ts +52 -0
  51. package/dist/esm/rsbuild/planning.js +108 -0
  52. package/dist/esm/rsbuild/planning.js.map +1 -0
  53. package/dist/esm/rsbuild/plugin.d.ts +4 -0
  54. package/dist/esm/rsbuild/plugin.js +344 -0
  55. package/dist/esm/rsbuild/plugin.js.map +1 -0
  56. package/dist/esm/rsbuild/post-build.d.ts +6 -0
  57. package/dist/esm/rsbuild/post-build.js +57 -0
  58. package/dist/esm/rsbuild/post-build.js.map +1 -0
  59. package/dist/esm/rsbuild/schema.d.ts +3372 -0
  60. package/dist/esm/rsbuild/schema.js +12 -0
  61. package/dist/esm/rsbuild/schema.js.map +1 -0
  62. package/dist/esm/rsbuild/start-compiler-host.d.ts +20 -0
  63. package/dist/esm/rsbuild/start-compiler-host.js +150 -0
  64. package/dist/esm/rsbuild/start-compiler-host.js.map +1 -0
  65. package/dist/esm/rsbuild/start-router-plugin.d.ts +18 -0
  66. package/dist/esm/rsbuild/start-router-plugin.js +63 -0
  67. package/dist/esm/rsbuild/start-router-plugin.js.map +1 -0
  68. package/dist/esm/rsbuild/swc-rsc.d.ts +14 -0
  69. package/dist/esm/rsbuild/swc-rsc.js +93 -0
  70. package/dist/esm/rsbuild/swc-rsc.js.map +1 -0
  71. package/dist/esm/rsbuild/types.d.ts +17 -0
  72. package/dist/esm/rsbuild/types.js +0 -0
  73. package/dist/esm/rsbuild/virtual-modules.d.ts +53 -0
  74. package/dist/esm/rsbuild/virtual-modules.js +287 -0
  75. package/dist/esm/rsbuild/virtual-modules.js.map +1 -0
  76. package/dist/esm/schema.d.ts +43 -43
  77. package/dist/esm/schema.js +1 -1
  78. package/dist/esm/start-compiler/compiler.d.ts +1 -1
  79. package/dist/esm/start-compiler/compiler.js +80 -9
  80. package/dist/esm/start-compiler/compiler.js.map +1 -1
  81. package/dist/esm/start-compiler/handleCreateServerFn.js +9 -0
  82. package/dist/esm/start-compiler/handleCreateServerFn.js.map +1 -1
  83. package/dist/esm/start-compiler/host.js +5 -1
  84. package/dist/esm/start-compiler/host.js.map +1 -1
  85. package/dist/esm/start-compiler/types.d.ts +1 -0
  86. package/dist/esm/start-manifest-plugin/manifestBuilder.d.ts +3 -6
  87. package/dist/esm/start-manifest-plugin/manifestBuilder.js +34 -81
  88. package/dist/esm/start-manifest-plugin/manifestBuilder.js.map +1 -1
  89. package/dist/esm/utils.d.ts +1 -0
  90. package/dist/esm/utils.js +4 -1
  91. package/dist/esm/utils.js.map +1 -1
  92. package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/plugin.js +41 -92
  93. package/dist/esm/vite/import-protection-plugin/plugin.js.map +1 -0
  94. package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/types.d.ts +5 -5
  95. package/dist/esm/vite/import-protection-plugin/virtualModules.d.ts +8 -0
  96. package/dist/esm/vite/import-protection-plugin/virtualModules.js +49 -0
  97. package/dist/esm/vite/import-protection-plugin/virtualModules.js.map +1 -0
  98. package/dist/esm/vite/plugin.js +4 -12
  99. package/dist/esm/vite/plugin.js.map +1 -1
  100. package/dist/esm/vite/plugins.d.ts +1 -5
  101. package/dist/esm/vite/plugins.js +2 -17
  102. package/dist/esm/vite/plugins.js.map +1 -1
  103. package/dist/esm/vite/post-server-build.js +14 -32
  104. package/dist/esm/vite/post-server-build.js.map +1 -1
  105. package/dist/esm/vite/prerender.d.ts +2 -2
  106. package/dist/esm/vite/prerender.js +17 -147
  107. package/dist/esm/vite/prerender.js.map +1 -1
  108. package/dist/esm/vite/schema.d.ts +23 -23
  109. package/dist/esm/vite/start-compiler-plugin/hot-update.d.ts +2 -0
  110. package/dist/esm/vite/start-compiler-plugin/hot-update.js +16 -0
  111. package/dist/esm/vite/start-compiler-plugin/hot-update.js.map +1 -0
  112. package/dist/esm/vite/start-compiler-plugin/module-specifier.js +9 -4
  113. package/dist/esm/vite/start-compiler-plugin/module-specifier.js.map +1 -1
  114. package/dist/esm/vite/start-compiler-plugin/plugin.js +86 -13
  115. package/dist/esm/vite/start-compiler-plugin/plugin.js.map +1 -1
  116. package/dist/esm/vite/start-manifest-plugin/normalized-client-build.js +2 -2
  117. package/dist/esm/vite/start-manifest-plugin/normalized-client-build.js.map +1 -1
  118. package/dist/esm/vite/start-manifest-plugin/plugin.d.ts +1 -2
  119. package/dist/esm/vite/start-manifest-plugin/plugin.js +48 -14
  120. package/dist/esm/vite/start-manifest-plugin/plugin.js.map +1 -1
  121. package/package.json +17 -4
  122. package/src/import-protection/INTERNALS.md +266 -0
  123. package/src/import-protection/adapterUtils.ts +94 -0
  124. package/src/import-protection/analysis.ts +853 -0
  125. package/src/{import-protection-plugin → import-protection}/constants.ts +7 -0
  126. package/src/import-protection/rewrite.ts +229 -0
  127. package/src/{import-protection-plugin → import-protection}/sourceLocation.ts +125 -9
  128. package/src/{import-protection-plugin → import-protection}/trace.ts +0 -1
  129. package/src/{import-protection-plugin → import-protection}/utils.ts +35 -20
  130. package/src/{import-protection-plugin → import-protection}/virtualModules.ts +30 -177
  131. package/src/index.ts +5 -0
  132. package/src/post-build.ts +64 -0
  133. package/src/prerender.ts +292 -0
  134. package/src/rsbuild/INTERNALS-import-protection.md +169 -0
  135. package/src/rsbuild/dev-server.ts +129 -0
  136. package/src/rsbuild/import-protection.ts +1600 -0
  137. package/src/rsbuild/normalized-client-build.ts +346 -0
  138. package/src/rsbuild/planning.ts +234 -0
  139. package/src/rsbuild/plugin.ts +754 -0
  140. package/src/rsbuild/post-build.ts +96 -0
  141. package/src/rsbuild/schema.ts +31 -0
  142. package/src/rsbuild/start-compiler-host.ts +250 -0
  143. package/src/rsbuild/start-router-plugin.ts +86 -0
  144. package/src/rsbuild/swc-rsc.ts +166 -0
  145. package/src/rsbuild/types.ts +20 -0
  146. package/src/rsbuild/virtual-modules.ts +565 -0
  147. package/src/start-compiler/compiler.ts +153 -19
  148. package/src/start-compiler/handleCreateServerFn.ts +18 -0
  149. package/src/start-compiler/types.ts +1 -0
  150. package/src/start-manifest-plugin/manifestBuilder.ts +53 -116
  151. package/src/utils.ts +4 -0
  152. package/src/vite/import-protection-plugin/INTERNALS.md +187 -0
  153. package/src/{import-protection-plugin → vite/import-protection-plugin}/plugin.ts +73 -158
  154. package/src/{import-protection-plugin → vite/import-protection-plugin}/types.ts +5 -5
  155. package/src/vite/import-protection-plugin/virtualModules.ts +122 -0
  156. package/src/vite/plugin.ts +1 -18
  157. package/src/vite/plugins.ts +2 -33
  158. package/src/vite/post-server-build.ts +14 -57
  159. package/src/vite/prerender.ts +19 -260
  160. package/src/vite/start-compiler-plugin/hot-update.ts +24 -0
  161. package/src/vite/start-compiler-plugin/module-specifier.ts +15 -5
  162. package/src/vite/start-compiler-plugin/plugin.ts +193 -18
  163. package/src/vite/start-manifest-plugin/normalized-client-build.ts +15 -16
  164. package/src/vite/start-manifest-plugin/plugin.ts +121 -38
  165. package/dist/esm/import-protection-plugin/ast.js.map +0 -1
  166. package/dist/esm/import-protection-plugin/constants.d.ts +0 -6
  167. package/dist/esm/import-protection-plugin/constants.js.map +0 -1
  168. package/dist/esm/import-protection-plugin/defaults.js.map +0 -1
  169. package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js.map +0 -1
  170. package/dist/esm/import-protection-plugin/matchers.js.map +0 -1
  171. package/dist/esm/import-protection-plugin/plugin.js.map +0 -1
  172. package/dist/esm/import-protection-plugin/postCompileUsage.d.ts +0 -13
  173. package/dist/esm/import-protection-plugin/postCompileUsage.js +0 -63
  174. package/dist/esm/import-protection-plugin/postCompileUsage.js.map +0 -1
  175. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js +0 -205
  176. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js.map +0 -1
  177. package/dist/esm/import-protection-plugin/sourceLocation.js.map +0 -1
  178. package/dist/esm/import-protection-plugin/trace.js.map +0 -1
  179. package/dist/esm/import-protection-plugin/utils.js.map +0 -1
  180. package/dist/esm/import-protection-plugin/virtualModules.d.ts +0 -78
  181. package/dist/esm/import-protection-plugin/virtualModules.js.map +0 -1
  182. package/dist/esm/start-compiler/load-module.d.ts +0 -14
  183. package/dist/esm/start-compiler/load-module.js +0 -18
  184. package/dist/esm/start-compiler/load-module.js.map +0 -1
  185. package/src/import-protection-plugin/INTERNALS.md +0 -700
  186. package/src/import-protection-plugin/postCompileUsage.ts +0 -100
  187. package/src/import-protection-plugin/rewriteDeniedImports.ts +0 -379
  188. package/src/start-compiler/load-module.ts +0 -31
  189. /package/dist/esm/{import-protection-plugin → import-protection}/ast.d.ts +0 -0
  190. /package/dist/esm/{import-protection-plugin → import-protection}/defaults.d.ts +0 -0
  191. /package/dist/esm/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.d.ts +0 -0
  192. /package/dist/esm/{import-protection-plugin → import-protection}/matchers.d.ts +0 -0
  193. /package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/plugin.d.ts +0 -0
  194. /package/src/{import-protection-plugin → import-protection}/ast.ts +0 -0
  195. /package/src/{import-protection-plugin → import-protection}/defaults.ts +0 -0
  196. /package/src/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.ts +0 -0
  197. /package/src/{import-protection-plugin → import-protection}/matchers.ts +0 -0
@@ -13,6 +13,7 @@ import { handleCreateMiddleware } from './handleCreateMiddleware'
13
13
  import { handleCreateIsomorphicFn } from './handleCreateIsomorphicFn'
14
14
  import { handleEnvOnlyFn } from './handleEnvOnly'
15
15
  import { handleClientOnlyJSX } from './handleClientOnlyJSX'
16
+ import { cleanId } from './utils'
16
17
  import type {
17
18
  CompilationContext,
18
19
  DevServerFnModuleSpecifierEncoder,
@@ -259,6 +260,19 @@ function isNestedDirectCallCandidate(node: t.CallExpression): boolean {
259
260
  return calleeName !== undefined && DirectCallFactoryNames.has(calleeName)
260
261
  }
261
262
 
263
+ function isSimpleDirectCallExpression(node: t.CallExpression): boolean {
264
+ return (
265
+ t.isIdentifier(node.callee) ||
266
+ (t.isMemberExpression(node.callee) &&
267
+ t.isIdentifier(node.callee.object) &&
268
+ t.isIdentifier(node.callee.property))
269
+ )
270
+ }
271
+
272
+ function isTopLevelDirectCallCandidateNode(node: t.CallExpression): boolean {
273
+ return isSimpleDirectCallExpression(node)
274
+ }
275
+
262
276
  /**
263
277
  * Checks if a CallExpression path is a top-level direct-call candidate.
264
278
  * Top-level means the call is the init of a VariableDeclarator at program level.
@@ -272,13 +286,7 @@ function isTopLevelDirectCallCandidate(
272
286
  const node = path.node
273
287
 
274
288
  // Must be a simple identifier call or namespace call
275
- const isSimpleCall =
276
- t.isIdentifier(node.callee) ||
277
- (t.isMemberExpression(node.callee) &&
278
- t.isIdentifier(node.callee.object) &&
279
- t.isIdentifier(node.callee.property))
280
-
281
- if (!isSimpleCall) {
289
+ if (!isSimpleDirectCallExpression(node)) {
282
290
  return false
283
291
  }
284
292
 
@@ -294,6 +302,12 @@ function isTopLevelDirectCallCandidate(
294
302
  return t.isProgram(path.parentPath.parentPath?.parent)
295
303
  }
296
304
 
305
+ function isDirectCallCandidateForKind(
306
+ kind: Exclude<LookupKind, 'ClientOnlyJSX'>,
307
+ ): boolean {
308
+ return LookupSetup[kind].type === 'directCall'
309
+ }
310
+
297
311
  export class StartCompiler {
298
312
  private moduleCache = new Map<string, ModuleInfo>()
299
313
  private initialized = false
@@ -530,7 +544,6 @@ export class StartCompiler {
530
544
 
531
545
  /**
532
546
  * Extracts bindings and exports from an already-parsed AST.
533
- * This is the core logic shared by ingestModule and ingestModuleFromAst.
534
547
  */
535
548
  private extractModuleInfo(
536
549
  ast: ReturnType<typeof parseAst>,
@@ -642,10 +655,105 @@ export class StartCompiler {
642
655
  }
643
656
 
644
657
  public invalidateModule(id: string) {
645
- // Note: Resolution caches (resolveIdCache, exportResolutionCache) are only
646
- // used in build mode where there's no HMR. In dev mode, caching is disabled,
647
- // so we only need to invalidate the moduleCache here.
648
- return this.moduleCache.delete(id)
658
+ const normalizedId = cleanId(id)
659
+ let hasCachedModule = false
660
+
661
+ for (const moduleId of Array.from(this.moduleCache.keys())) {
662
+ if (cleanId(moduleId) === normalizedId) {
663
+ this.moduleCache.delete(moduleId)
664
+ hasCachedModule = true
665
+ }
666
+ }
667
+
668
+ // Root import metadata is synthetic compiler state and should survive HMR.
669
+ // The stale dev state lives in per-module resolvedKind memoization.
670
+ for (const [moduleId, moduleInfo] of this.moduleCache) {
671
+ if (this.knownRootImports.has(moduleId)) {
672
+ continue
673
+ }
674
+
675
+ for (const binding of moduleInfo.bindings.values()) {
676
+ binding.resolvedKind = undefined
677
+ }
678
+ }
679
+
680
+ // Build-mode caches are cheap to rebuild and may point at removed entries.
681
+ this.resolveIdCache.clear()
682
+ this.exportResolutionCache.clear()
683
+
684
+ return hasCachedModule
685
+ }
686
+
687
+ public async getTransitiveImporters(id: string): Promise<Set<string>> {
688
+ const discoveredImporters = new Set<string>()
689
+ const pendingTargets = [cleanId(id)]
690
+ const visitedTargets = new Set<string>()
691
+ const resolveCache = new Map<string, Promise<string | null>>()
692
+
693
+ const resolveSource = (source: string, importer: string) => {
694
+ const cacheKey = `${importer}::${source}`
695
+ let resolved = resolveCache.get(cacheKey)
696
+
697
+ if (!resolved) {
698
+ resolved = this.resolveIdCached(source, importer)
699
+ resolveCache.set(cacheKey, resolved)
700
+ }
701
+
702
+ return resolved
703
+ }
704
+
705
+ while (pendingTargets.length > 0) {
706
+ const targetId = pendingTargets.pop()!
707
+
708
+ if (visitedTargets.has(targetId)) {
709
+ continue
710
+ }
711
+
712
+ visitedTargets.add(targetId)
713
+
714
+ const importerIds = await Promise.all(
715
+ Array.from(this.moduleCache.values()).map(async (moduleInfo) => {
716
+ if (this.knownRootImports.has(moduleInfo.id)) {
717
+ return null
718
+ }
719
+
720
+ const moduleId = cleanId(moduleInfo.id)
721
+
722
+ if (moduleId === targetId) {
723
+ return null
724
+ }
725
+
726
+ const importSources = new Set(moduleInfo.reExportAllSources)
727
+
728
+ for (const binding of moduleInfo.bindings.values()) {
729
+ if (binding.type === 'import') {
730
+ importSources.add(binding.source)
731
+ }
732
+ }
733
+
734
+ for (const source of importSources) {
735
+ const resolved = await resolveSource(source, moduleInfo.id)
736
+
737
+ if (resolved && cleanId(resolved) === targetId) {
738
+ return moduleId
739
+ }
740
+ }
741
+
742
+ return null
743
+ }),
744
+ )
745
+
746
+ for (const importerId of importerIds) {
747
+ if (!importerId || discoveredImporters.has(importerId)) {
748
+ continue
749
+ }
750
+
751
+ discoveredImporters.add(importerId)
752
+ pendingTargets.push(importerId)
753
+ }
754
+ }
755
+
756
+ return discoveredImporters
649
757
  }
650
758
 
651
759
  public async compile({
@@ -719,7 +827,10 @@ export class StartCompiler {
719
827
  if (declarations) {
720
828
  for (const decl of declarations) {
721
829
  if (decl.init && t.isCallExpression(decl.init)) {
722
- if (isMethodChainCandidate(decl.init, fileKinds)) {
830
+ if (
831
+ isMethodChainCandidate(decl.init, fileKinds) ||
832
+ isTopLevelDirectCallCandidateNode(decl.init)
833
+ ) {
723
834
  candidateIndices.push(i)
724
835
  break // Only need to mark this statement once
725
836
  }
@@ -760,6 +871,11 @@ export class StartCompiler {
760
871
  // Method chain pattern
761
872
  if (isMethodChainCandidate(node, fileKinds)) {
762
873
  candidatePaths.push(path)
874
+ return
875
+ }
876
+
877
+ if (isTopLevelDirectCallCandidate(path)) {
878
+ candidatePaths.push(path)
763
879
  }
764
880
  },
765
881
  })
@@ -792,11 +908,14 @@ export class StartCompiler {
792
908
  return
793
909
  }
794
910
 
911
+ if (isTopLevelDirectCallCandidate(path)) {
912
+ candidatePaths.push(path)
913
+ return
914
+ }
915
+
795
916
  // Pattern 2: Direct call pattern
796
917
  if (checkDirectCalls) {
797
- if (isTopLevelDirectCallCandidate(path)) {
798
- candidatePaths.push(path)
799
- } else if (isNestedDirectCallCandidate(node)) {
918
+ if (isNestedDirectCallCandidate(node)) {
800
919
  candidatePaths.push(path)
801
920
  }
802
921
  }
@@ -845,9 +964,23 @@ export class StartCompiler {
845
964
  )
846
965
 
847
966
  // Filter to valid candidates
848
- const validCandidates = resolvedCandidates.filter(({ kind }) =>
849
- this.validLookupKinds.has(kind as Exclude<LookupKind, 'ClientOnlyJSX'>),
850
- ) as Array<{
967
+ const validCandidates = resolvedCandidates.filter(({ path, kind }) => {
968
+ if (
969
+ !this.validLookupKinds.has(kind as Exclude<LookupKind, 'ClientOnlyJSX'>)
970
+ ) {
971
+ return false
972
+ }
973
+
974
+ if (
975
+ isLookupKind(kind) &&
976
+ kind !== 'ClientOnlyJSX' &&
977
+ !isMethodChainCandidate(path.node, fileKinds)
978
+ ) {
979
+ return isDirectCallCandidateForKind(kind)
980
+ }
981
+
982
+ return true
983
+ }) as Array<{
851
984
  path: babel.NodePath<t.CallExpression>
852
985
  kind: Exclude<LookupKind, 'ClientOnlyJSX'>
853
986
  }>
@@ -925,6 +1058,7 @@ export class StartCompiler {
925
1058
  code,
926
1059
  env: this.options.env,
927
1060
  envName: this.options.envName,
1061
+ mode: this.mode,
928
1062
  root: this.options.root,
929
1063
  framework: this.options.framework,
930
1064
  providerEnvName: this.options.providerEnvName,
@@ -7,6 +7,20 @@ import type { CompileStartFrameworkOptions } from '../types'
7
7
 
8
8
  const TSS_SERVERFN_SPLIT_PARAM = 'tss-serverfn-split'
9
9
 
10
+ const providerHmrAcceptTemplate = babel.template.statements(
11
+ `
12
+ if (import.meta.hot) {
13
+ import.meta.hot.accept(() => {})
14
+ }
15
+ if (import.meta.webpackHot) {
16
+ import.meta.webpackHot.accept(() => {})
17
+ }
18
+ `,
19
+ {
20
+ placeholderPattern: false,
21
+ },
22
+ )
23
+
10
24
  // ============================================================================
11
25
  // Pre-compiled babel templates (compiled once at module load time)
12
26
  // ============================================================================
@@ -414,6 +428,10 @@ export function handleCreateServerFn(
414
428
  ),
415
429
  )
416
430
  }
431
+
432
+ if (context.mode === 'dev') {
433
+ context.ast.program.body.push(...providerHmrAcceptTemplate())
434
+ }
417
435
  }
418
436
 
419
437
  // Notify about discovered functions (only for non-provider files)
@@ -12,6 +12,7 @@ export interface CompilationContext {
12
12
  readonly id: string
13
13
  readonly env: 'client' | 'server'
14
14
  readonly envName: string
15
+ readonly mode: 'dev' | 'build'
15
16
  readonly root: string
16
17
  /** The framework being used (e.g., 'react', 'solid') */
17
18
  readonly framework: CompileStartFrameworkOptions
@@ -10,8 +10,6 @@ import {
10
10
  import type { ManifestAssetLink, RouterManagedTag } from '@tanstack/router-core'
11
11
  import type { NormalizedClientBuild, NormalizedClientChunk } from '../types'
12
12
 
13
- const ROUTER_MANAGED_MODE = 1
14
- const NON_ROUTE_DYNAMIC_MODE = 2
15
13
  const VISITING_CHUNK = 1
16
14
 
17
15
  type RouteTreeRoute = {
@@ -27,7 +25,6 @@ interface ScannedClientChunks {
27
25
  entryChunk: NormalizedClientChunk
28
26
  chunksByFileName: ReadonlyMap<string, NormalizedClientChunk>
29
27
  routeChunksByFilePath: ReadonlyMap<string, Array<NormalizedClientChunk>>
30
- routeEntryChunks: ReadonlySet<NormalizedClientChunk>
31
28
  }
32
29
 
33
30
  interface ManifestAssetResolvers {
@@ -114,19 +111,25 @@ export function appendUniqueAssets(
114
111
  }
115
112
 
116
113
  function getAssetIdentity(asset: RouterManagedTag) {
117
- if (asset.tag === 'link' || asset.tag === 'script') {
118
- const attrs = asset.attrs ?? {}
119
- return [
120
- asset.tag,
121
- 'href' in attrs ? String(attrs.href) : '',
122
- 'src' in attrs ? String(attrs.src) : '',
123
- 'rel' in attrs ? String(attrs.rel) : '',
124
- 'type' in attrs ? String(attrs.type) : '',
125
- asset.children ?? '',
126
- ].join('|')
114
+ return JSON.stringify({
115
+ tag: asset.tag,
116
+ attrs: normalizeAssetAttrs(asset.attrs),
117
+ children: 'children' in asset ? (asset.children ?? null) : null,
118
+ })
119
+ }
120
+
121
+ function normalizeAssetAttrs(attrs: Record<string, any> | undefined) {
122
+ if (!attrs) {
123
+ return null
127
124
  }
128
125
 
129
- return JSON.stringify(asset)
126
+ const entries = Object.entries(attrs)
127
+ if (entries.length === 0) {
128
+ return null
129
+ }
130
+
131
+ entries.sort(([left], [right]) => left.localeCompare(right))
132
+ return Object.fromEntries(entries)
130
133
  }
131
134
 
132
135
  function mergeRouteChunkData(options: {
@@ -149,17 +152,12 @@ export function buildStartManifest(options: {
149
152
  clientBuild: NormalizedClientBuild
150
153
  routeTreeRoutes: RouteTreeRoutes
151
154
  basePath: string
155
+ additionalRouteAssets?: Partial<
156
+ Record<string, ReadonlyArray<RouterManagedTag>>
157
+ >
152
158
  }): StartManifest {
153
159
  const scannedChunks = scanClientChunks(options.clientBuild)
154
- const hashedCssFiles = collectDynamicImportCss(
155
- scannedChunks.routeEntryChunks,
156
- scannedChunks.chunksByFileName,
157
- scannedChunks.entryChunk,
158
- )
159
- const assetResolvers = createManifestAssetResolvers({
160
- basePath: options.basePath,
161
- hashedCssFiles,
162
- })
160
+ const assetResolvers = createManifestAssetResolvers(options.basePath)
163
161
 
164
162
  const routes = buildRouteManifestRoutes({
165
163
  routeTreeRoutes: options.routeTreeRoutes,
@@ -167,6 +165,7 @@ export function buildStartManifest(options: {
167
165
  chunksByFileName: scannedChunks.chunksByFileName,
168
166
  entryChunk: scannedChunks.entryChunk,
169
167
  assetResolvers,
168
+ additionalRouteAssets: options.additionalRouteAssets,
170
169
  })
171
170
 
172
171
  dedupeNestedRouteManifestEntries(rootRouteId, routes[rootRouteId]!, routes)
@@ -202,13 +201,10 @@ export function scanClientChunks(
202
201
  throw new Error(`Missing entry chunk: ${clientBuild.entryChunkFileName}`)
203
202
  }
204
203
 
205
- const routeEntryChunks = new Set<NormalizedClientChunk>()
206
204
  const routeChunksByFilePath = new Map<string, Array<NormalizedClientChunk>>()
207
205
 
208
206
  for (const chunk of clientBuild.chunksByFileName.values()) {
209
207
  if (chunk.routeFilePaths.length > 0) {
210
- routeEntryChunks.add(chunk)
211
-
212
208
  for (const routeFilePath of chunk.routeFilePaths) {
213
209
  let chunks = routeChunksByFilePath.get(routeFilePath)
214
210
  if (chunks === undefined) {
@@ -224,92 +220,12 @@ export function scanClientChunks(
224
220
  entryChunk,
225
221
  chunksByFileName: clientBuild.chunksByFileName,
226
222
  routeChunksByFilePath,
227
- routeEntryChunks,
228
223
  }
229
224
  }
230
225
 
231
- export function collectDynamicImportCss(
232
- routeEntryChunks: ReadonlySet<NormalizedClientChunk>,
233
- chunksByFileName: ReadonlyMap<string, NormalizedClientChunk>,
234
- entryChunk?: NormalizedClientChunk,
235
- ) {
236
- const routerManagedCssFiles = new Set<string>()
237
- const nonRouteDynamicCssFiles = new Set<string>()
238
- const hashedCssFiles = new Set<string>()
239
- const visitedByChunk = new Map<NormalizedClientChunk, number>()
240
- const chunkStack: Array<NormalizedClientChunk> = []
241
- const modeStack: Array<number> = []
242
-
243
- for (const routeEntryChunk of routeEntryChunks) {
244
- chunkStack.push(routeEntryChunk)
245
- modeStack.push(ROUTER_MANAGED_MODE)
246
- }
247
-
248
- if (entryChunk) {
249
- chunkStack.push(entryChunk)
250
- modeStack.push(ROUTER_MANAGED_MODE)
251
- }
252
-
253
- while (chunkStack.length > 0) {
254
- const chunk = chunkStack.pop()!
255
- const mode = modeStack.pop()!
256
- const previousMode = visitedByChunk.get(chunk) ?? 0
257
-
258
- if ((previousMode & mode) === mode) {
259
- continue
260
- }
261
-
262
- visitedByChunk.set(chunk, previousMode | mode)
263
-
264
- if ((mode & ROUTER_MANAGED_MODE) !== 0) {
265
- for (const cssFile of chunk.css) {
266
- routerManagedCssFiles.add(cssFile)
267
- }
268
- }
269
-
270
- if ((mode & NON_ROUTE_DYNAMIC_MODE) !== 0) {
271
- for (const cssFile of chunk.css) {
272
- nonRouteDynamicCssFiles.add(cssFile)
273
- }
274
- }
275
-
276
- for (let i = 0; i < chunk.imports.length; i++) {
277
- const importedChunk = chunksByFileName.get(chunk.imports[i]!)
278
- if (importedChunk) {
279
- chunkStack.push(importedChunk)
280
- modeStack.push(mode)
281
- }
282
- }
283
-
284
- for (let i = 0; i < chunk.dynamicImports.length; i++) {
285
- const dynamicImportedChunk = chunksByFileName.get(
286
- chunk.dynamicImports[i]!,
287
- )
288
- if (dynamicImportedChunk) {
289
- chunkStack.push(dynamicImportedChunk)
290
- modeStack.push(
291
- (mode & NON_ROUTE_DYNAMIC_MODE) !== 0 ||
292
- !routeEntryChunks.has(dynamicImportedChunk)
293
- ? NON_ROUTE_DYNAMIC_MODE
294
- : ROUTER_MANAGED_MODE,
295
- )
296
- }
297
- }
298
- }
299
-
300
- for (const cssFile of routerManagedCssFiles) {
301
- if (nonRouteDynamicCssFiles.has(cssFile)) {
302
- hashedCssFiles.add(cssFile)
303
- }
304
- }
305
-
306
- return hashedCssFiles
307
- }
308
-
309
- export function createManifestAssetResolvers(options: {
310
- basePath: string
311
- hashedCssFiles?: Set<string>
312
- }): ManifestAssetResolvers {
226
+ export function createManifestAssetResolvers(
227
+ basePath: string,
228
+ ): ManifestAssetResolvers {
313
229
  const assetPathByFileName = new Map<string, string>()
314
230
  const stylesheetAssetByFileName = new Map<string, RouterManagedTag>()
315
231
  const preloadsByChunk = new Map<NormalizedClientChunk, Array<string>>()
@@ -320,7 +236,7 @@ export function createManifestAssetResolvers(options: {
320
236
  return cachedPath
321
237
  }
322
238
 
323
- const assetPath = joinURL(options.basePath, fileName)
239
+ const assetPath = joinURL(basePath, fileName)
324
240
  assetPathByFileName.set(fileName, assetPath)
325
241
  return assetPath
326
242
  }
@@ -336,7 +252,7 @@ export function createManifestAssetResolvers(options: {
336
252
  tag: 'link',
337
253
  attrs: {
338
254
  rel: 'stylesheet',
339
- href: options.hashedCssFiles?.has(cssFile) ? `${href}#` : href,
255
+ href,
340
256
  type: 'text/css',
341
257
  },
342
258
  } satisfies RouterManagedTag
@@ -380,15 +296,14 @@ export function createChunkCssAssetCollector(options: {
380
296
 
381
297
  const appendAsset = (
382
298
  assets: Array<RouterManagedTag>,
383
- seenAssets: Set<string>,
299
+ seenAssets: Set<RouterManagedTag>,
384
300
  asset: RouterManagedTag,
385
301
  ) => {
386
- const identity = getAssetIdentity(asset)
387
- if (seenAssets.has(identity)) {
302
+ if (seenAssets.has(asset)) {
388
303
  return
389
304
  }
390
305
 
391
- seenAssets.add(identity)
306
+ seenAssets.add(asset)
392
307
  assets.push(asset)
393
308
  }
394
309
 
@@ -406,7 +321,7 @@ export function createChunkCssAssetCollector(options: {
406
321
  stateByChunk.set(chunk, VISITING_CHUNK)
407
322
 
408
323
  const assets: Array<RouterManagedTag> = []
409
- const seenAssets = new Set<string>()
324
+ const seenAssets = new Set<RouterManagedTag>()
410
325
 
411
326
  for (const cssFile of chunk.css) {
412
327
  appendAsset(assets, seenAssets, options.getStylesheetAsset(cssFile))
@@ -441,6 +356,9 @@ export function buildRouteManifestRoutes(options: {
441
356
  chunksByFileName: ReadonlyMap<string, NormalizedClientChunk>
442
357
  entryChunk: NormalizedClientChunk
443
358
  assetResolvers: ManifestAssetResolvers
359
+ additionalRouteAssets?: Partial<
360
+ Record<string, ReadonlyArray<RouterManagedTag>>
361
+ >
444
362
  }) {
445
363
  const routes: Record<string, RouteTreeRoute> = {}
446
364
  const getChunkCssAssets = createChunkCssAssetCollector({
@@ -485,6 +403,25 @@ export function buildRouteManifestRoutes(options: {
485
403
  getChunkPreloads: options.assetResolvers.getChunkPreloads,
486
404
  })
487
405
 
406
+ if (options.additionalRouteAssets) {
407
+ for (const [routeId, assets] of Object.entries(
408
+ options.additionalRouteAssets,
409
+ )) {
410
+ if (!assets || assets.length === 0) {
411
+ continue
412
+ }
413
+
414
+ if (!(routeId in options.routeTreeRoutes)) {
415
+ throw new Error(
416
+ `expected additionalRouteAssets routeId to exist in routeTreeRoutes: ${routeId}`,
417
+ )
418
+ }
419
+
420
+ const route = (routes[routeId] = routes[routeId] || {})
421
+ route.assets = appendUniqueAssets(route.assets, [...assets])
422
+ }
423
+ }
424
+
488
425
  return routes
489
426
  }
490
427
 
package/src/utils.ts CHANGED
@@ -17,3 +17,7 @@ export function createLogger(prefix: string) {
17
17
  error: (...args: any) => console.error(label, ...args),
18
18
  }
19
19
  }
20
+
21
+ export function normalizePath(path: string): string {
22
+ return path.replace(/\\/g, '/')
23
+ }