@tanstack/start-plugin-core 1.167.35 → 1.169.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 (187) 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 +2 -2
  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 +13 -20
  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 +1 -5
  34. package/dist/esm/index.js +2 -4
  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/index.d.ts +4 -0
  48. package/dist/esm/rsbuild/index.js +3 -0
  49. package/dist/esm/rsbuild/normalized-client-build.d.ts +18 -0
  50. package/dist/esm/rsbuild/normalized-client-build.js +207 -0
  51. package/dist/esm/rsbuild/normalized-client-build.js.map +1 -0
  52. package/dist/esm/rsbuild/planning.d.ts +52 -0
  53. package/dist/esm/rsbuild/planning.js +108 -0
  54. package/dist/esm/rsbuild/planning.js.map +1 -0
  55. package/dist/esm/rsbuild/plugin.d.ts +4 -0
  56. package/dist/esm/rsbuild/plugin.js +344 -0
  57. package/dist/esm/rsbuild/plugin.js.map +1 -0
  58. package/dist/esm/rsbuild/post-build.d.ts +6 -0
  59. package/dist/esm/rsbuild/post-build.js +57 -0
  60. package/dist/esm/rsbuild/post-build.js.map +1 -0
  61. package/dist/esm/rsbuild/schema.d.ts +3372 -0
  62. package/dist/esm/rsbuild/schema.js +12 -0
  63. package/dist/esm/rsbuild/schema.js.map +1 -0
  64. package/dist/esm/rsbuild/start-compiler-host.d.ts +20 -0
  65. package/dist/esm/rsbuild/start-compiler-host.js +150 -0
  66. package/dist/esm/rsbuild/start-compiler-host.js.map +1 -0
  67. package/dist/esm/rsbuild/start-router-plugin.d.ts +18 -0
  68. package/dist/esm/rsbuild/start-router-plugin.js +63 -0
  69. package/dist/esm/rsbuild/start-router-plugin.js.map +1 -0
  70. package/dist/esm/rsbuild/swc-rsc.d.ts +14 -0
  71. package/dist/esm/rsbuild/swc-rsc.js +93 -0
  72. package/dist/esm/rsbuild/swc-rsc.js.map +1 -0
  73. package/dist/esm/rsbuild/types.d.ts +17 -0
  74. package/dist/esm/rsbuild/types.js +0 -0
  75. package/dist/esm/rsbuild/virtual-modules.d.ts +53 -0
  76. package/dist/esm/rsbuild/virtual-modules.js +287 -0
  77. package/dist/esm/rsbuild/virtual-modules.js.map +1 -0
  78. package/dist/esm/schema.d.ts +43 -43
  79. package/dist/esm/start-compiler/compiler.d.ts +1 -1
  80. package/dist/esm/start-compiler/compiler.js +80 -9
  81. package/dist/esm/start-compiler/compiler.js.map +1 -1
  82. package/dist/esm/start-compiler/handleCreateServerFn.js +9 -0
  83. package/dist/esm/start-compiler/handleCreateServerFn.js.map +1 -1
  84. package/dist/esm/start-compiler/host.js +5 -1
  85. package/dist/esm/start-compiler/host.js.map +1 -1
  86. package/dist/esm/start-compiler/types.d.ts +1 -0
  87. package/dist/esm/utils.d.ts +1 -0
  88. package/dist/esm/utils.js +10 -1
  89. package/dist/esm/utils.js.map +1 -1
  90. package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/plugin.js +41 -92
  91. package/dist/esm/vite/import-protection-plugin/plugin.js.map +1 -0
  92. package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/types.d.ts +5 -5
  93. package/dist/esm/vite/import-protection-plugin/virtualModules.d.ts +8 -0
  94. package/dist/esm/vite/import-protection-plugin/virtualModules.js +49 -0
  95. package/dist/esm/vite/import-protection-plugin/virtualModules.js.map +1 -0
  96. package/dist/esm/vite/index.d.ts +5 -0
  97. package/dist/esm/vite/index.js +4 -0
  98. package/dist/esm/vite/plugin.js +1 -1
  99. package/dist/esm/vite/plugin.js.map +1 -1
  100. package/dist/esm/vite/post-server-build.js +14 -32
  101. package/dist/esm/vite/post-server-build.js.map +1 -1
  102. package/dist/esm/vite/prerender.d.ts +2 -2
  103. package/dist/esm/vite/prerender.js +17 -147
  104. package/dist/esm/vite/prerender.js.map +1 -1
  105. package/dist/esm/vite/schema.d.ts +23 -23
  106. package/dist/esm/vite/start-compiler-plugin/hot-update.d.ts +2 -0
  107. package/dist/esm/vite/start-compiler-plugin/hot-update.js +16 -0
  108. package/dist/esm/vite/start-compiler-plugin/hot-update.js.map +1 -0
  109. package/dist/esm/vite/start-compiler-plugin/module-specifier.js +9 -4
  110. package/dist/esm/vite/start-compiler-plugin/module-specifier.js.map +1 -1
  111. package/dist/esm/vite/start-compiler-plugin/plugin.js +86 -13
  112. package/dist/esm/vite/start-compiler-plugin/plugin.js.map +1 -1
  113. package/package.json +32 -4
  114. package/src/import-protection/INTERNALS.md +266 -0
  115. package/src/import-protection/adapterUtils.ts +94 -0
  116. package/src/import-protection/analysis.ts +853 -0
  117. package/src/{import-protection-plugin → import-protection}/constants.ts +7 -0
  118. package/src/import-protection/rewrite.ts +229 -0
  119. package/src/{import-protection-plugin → import-protection}/sourceLocation.ts +125 -9
  120. package/src/{import-protection-plugin → import-protection}/trace.ts +0 -1
  121. package/src/{import-protection-plugin → import-protection}/utils.ts +36 -21
  122. package/src/{import-protection-plugin → import-protection}/virtualModules.ts +30 -177
  123. package/src/index.ts +1 -8
  124. package/src/post-build.ts +64 -0
  125. package/src/prerender.ts +292 -0
  126. package/src/rsbuild/INTERNALS-import-protection.md +169 -0
  127. package/src/rsbuild/dev-server.ts +129 -0
  128. package/src/rsbuild/import-protection.ts +1599 -0
  129. package/src/rsbuild/index.ts +4 -0
  130. package/src/rsbuild/normalized-client-build.ts +346 -0
  131. package/src/rsbuild/planning.ts +234 -0
  132. package/src/rsbuild/plugin.ts +754 -0
  133. package/src/rsbuild/post-build.ts +96 -0
  134. package/src/rsbuild/schema.ts +31 -0
  135. package/src/rsbuild/start-compiler-host.ts +250 -0
  136. package/src/rsbuild/start-router-plugin.ts +86 -0
  137. package/src/rsbuild/swc-rsc.ts +166 -0
  138. package/src/rsbuild/types.ts +20 -0
  139. package/src/rsbuild/virtual-modules.ts +565 -0
  140. package/src/start-compiler/compiler.ts +153 -19
  141. package/src/start-compiler/handleCreateServerFn.ts +18 -0
  142. package/src/start-compiler/types.ts +1 -0
  143. package/src/utils.ts +14 -0
  144. package/src/vite/import-protection-plugin/INTERNALS.md +187 -0
  145. package/src/{import-protection-plugin → vite/import-protection-plugin}/plugin.ts +73 -158
  146. package/src/{import-protection-plugin → vite/import-protection-plugin}/types.ts +5 -5
  147. package/src/vite/import-protection-plugin/virtualModules.ts +122 -0
  148. package/src/vite/index.ts +8 -0
  149. package/src/vite/plugin.ts +1 -1
  150. package/src/vite/post-server-build.ts +14 -57
  151. package/src/vite/prerender.ts +19 -260
  152. package/src/vite/start-compiler-plugin/hot-update.ts +24 -0
  153. package/src/vite/start-compiler-plugin/module-specifier.ts +15 -5
  154. package/src/vite/start-compiler-plugin/plugin.ts +193 -18
  155. package/dist/esm/import-protection-plugin/ast.js.map +0 -1
  156. package/dist/esm/import-protection-plugin/constants.d.ts +0 -6
  157. package/dist/esm/import-protection-plugin/constants.js.map +0 -1
  158. package/dist/esm/import-protection-plugin/defaults.js.map +0 -1
  159. package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js.map +0 -1
  160. package/dist/esm/import-protection-plugin/matchers.js.map +0 -1
  161. package/dist/esm/import-protection-plugin/plugin.js.map +0 -1
  162. package/dist/esm/import-protection-plugin/postCompileUsage.d.ts +0 -13
  163. package/dist/esm/import-protection-plugin/postCompileUsage.js +0 -63
  164. package/dist/esm/import-protection-plugin/postCompileUsage.js.map +0 -1
  165. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js +0 -205
  166. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js.map +0 -1
  167. package/dist/esm/import-protection-plugin/sourceLocation.js.map +0 -1
  168. package/dist/esm/import-protection-plugin/trace.js.map +0 -1
  169. package/dist/esm/import-protection-plugin/utils.js.map +0 -1
  170. package/dist/esm/import-protection-plugin/virtualModules.d.ts +0 -78
  171. package/dist/esm/import-protection-plugin/virtualModules.js.map +0 -1
  172. package/dist/esm/start-compiler/load-module.d.ts +0 -14
  173. package/dist/esm/start-compiler/load-module.js +0 -18
  174. package/dist/esm/start-compiler/load-module.js.map +0 -1
  175. package/src/import-protection-plugin/INTERNALS.md +0 -700
  176. package/src/import-protection-plugin/postCompileUsage.ts +0 -100
  177. package/src/import-protection-plugin/rewriteDeniedImports.ts +0 -379
  178. package/src/start-compiler/load-module.ts +0 -31
  179. /package/dist/esm/{import-protection-plugin → import-protection}/ast.d.ts +0 -0
  180. /package/dist/esm/{import-protection-plugin → import-protection}/defaults.d.ts +0 -0
  181. /package/dist/esm/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.d.ts +0 -0
  182. /package/dist/esm/{import-protection-plugin → import-protection}/matchers.d.ts +0 -0
  183. /package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/plugin.d.ts +0 -0
  184. /package/src/{import-protection-plugin → import-protection}/ast.ts +0 -0
  185. /package/src/{import-protection-plugin → import-protection}/defaults.ts +0 -0
  186. /package/src/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.ts +0 -0
  187. /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
package/src/utils.ts CHANGED
@@ -1,3 +1,9 @@
1
+ import path from 'node:path'
2
+
3
+ const isWindows: boolean =
4
+ typeof process !== 'undefined' && process.platform === 'win32'
5
+ const windowsSlashRE = /\\/g
6
+
1
7
  /** Read `build.rollupOptions` or `build.rolldownOptions` from a build config. */
2
8
  export function getBundlerOptions(build: any): any {
3
9
  return build?.rolldownOptions ?? build?.rollupOptions
@@ -17,3 +23,11 @@ export function createLogger(prefix: string) {
17
23
  error: (...args: any) => console.error(label, ...args),
18
24
  }
19
25
  }
26
+
27
+ function slash(path: string): string {
28
+ return path.replace(windowsSlashRE, '/')
29
+ }
30
+
31
+ export function normalizePath(id: string): string {
32
+ return path.posix.normalize(isWindows ? slash(id) : id)
33
+ }
@@ -0,0 +1,187 @@
1
+ # Vite Import Protection - Adapter Internals
2
+
3
+ ## Scope
4
+
5
+ This document covers the Vite-specific orchestration around the shared
6
+ import-protection core in `src/import-protection/INTERNALS.md`.
7
+
8
+ Vite owns:
9
+
10
+ - `resolveId`-first violation discovery
11
+ - dev/build deferral decisions tied to Vite lifecycle hooks
12
+ - the transform-cache plugin and post-transform graph bookkeeping
13
+ - Vite virtual-module id resolution and loading
14
+ - bundle-time survival checks in `generateBundle`
15
+
16
+ Shared AST analysis, rewrite logic, source extraction, mock export discovery,
17
+ usage lookup, source locations, and mock code generation are described in the
18
+ shared internals doc.
19
+
20
+ ## Plugin Shape
21
+
22
+ `importProtectionPlugin()` returns two Vite plugins:
23
+
24
+ 1. `tanstack-start-core:import-protection`
25
+ 2. `tanstack-start-core:import-protection-transform-cache`
26
+
27
+ The first plugin owns resolve-time detection and reporting decisions. The
28
+ second plugin owns transformed-result caching, post-transform import graph
29
+ updates, self-denial transforms, and dev pending-violation verification.
30
+
31
+ ## Why Vite Uses Two Plugins
32
+
33
+ Vite discovers many violations in `resolveId`, but accurate source locations and
34
+ post-transform graph truth require transformed code that only exists later.
35
+
36
+ So the adapter splits responsibilities:
37
+
38
+ - `resolveId`: detect, classify, defer/report, substitute mock ids where needed
39
+ - later transform hook: cache transformed code/maps, self-deny files, compute
40
+ post-transform imports, and process pending violations
41
+
42
+ ## Vite State Model
43
+
44
+ Per environment, Vite keeps `EnvState` with:
45
+
46
+ - `graph`
47
+ - `mockExportsByImporter`
48
+ - `resolveCache`
49
+ - `resolveCacheByFile`
50
+ - `importLocCache`
51
+ - `seenViolations`
52
+ - `serverFnLookupModules`
53
+ - `transformResultCache`
54
+ - `transformResultKeysByFile`
55
+ - `transformResultProvider`
56
+ - `postTransformImports`
57
+ - `pendingViolations`
58
+ - `deferredBuildViolations`
59
+
60
+ Cross-environment shared state is intentionally small:
61
+
62
+ - `fileMarkerKind`
63
+
64
+ This is larger than the Rsbuild state model because Vite must bridge resolve-
65
+ time detection with later transform/bundle verification.
66
+
67
+ ## Detection Flow
68
+
69
+ Vite is `resolveId`-driven.
70
+
71
+ That means the adapter may see violations before:
72
+
73
+ - the Start compiler removes safe-boundary imports
74
+ - tree-shaking removes false-positive edges
75
+ - transformed importer code is available
76
+
77
+ So Vite must decide whether to report immediately or defer.
78
+
79
+ ## Deferral Policy
80
+
81
+ The shared helper is:
82
+
83
+ ```ts
84
+ shouldDefer = isBuild || isDevMock
85
+ ```
86
+
87
+ Adapter meaning:
88
+
89
+ - dev + error: report immediately with `ctx.error()`
90
+ - dev + mock: store `pendingViolations`, then verify later from post-transform
91
+ edges and reachability
92
+ - build + mock/error: store `deferredBuildViolations`, then verify in
93
+ `generateBundle`
94
+
95
+ This is the main adapter-specific state machine difference from Rsbuild.
96
+
97
+ ## Dev Strategy
98
+
99
+ ### Dev + Error
100
+
101
+ Vite throws immediately in `resolveId`.
102
+
103
+ This is intentionally aggressive and can still produce known barrel false
104
+ positives because there is no tree-shaking in the dev server.
105
+
106
+ Pre-transform resolve paths like `SERVER_FN_LOOKUP` are silenced in this mode
107
+ because there is no later deferred verification path.
108
+
109
+ ### Dev + Mock
110
+
111
+ Vite defers all violations into `pendingViolations`.
112
+
113
+ Later, the transform-cache plugin:
114
+
115
+ 1. caches transformed code and sourcemaps
116
+ 2. resolves post-transform imports
117
+ 3. records graph edges from transformed code
118
+ 4. runs `processPendingViolations()`
119
+
120
+ Pending verification uses two checks:
121
+
122
+ 1. edge survival after the Start compiler transform
123
+ 2. graph reachability from entries using post-transform edges
124
+
125
+ This is how Vite suppresses many dev false positives without bundling.
126
+
127
+ ## Build Strategy
128
+
129
+ Vite build uses mock-first, verify-later behavior.
130
+
131
+ At resolve time it substitutes mocks silently and records deferred violations.
132
+ At `generateBundle`, it checks whether each unique mock id survived tree-
133
+ shaking.
134
+
135
+ If the mock survived:
136
+
137
+ - error mode: fail the build
138
+ - mock mode: emit a warning
139
+
140
+ If the mock was removed, the violation is suppressed as a false positive.
141
+
142
+ ## Self-Denial In Vite
143
+
144
+ For file-based violations, `resolveId` does not return a virtual mock id.
145
+ Instead it returns the physical file path, and the later transform-cache plugin
146
+ replaces the file contents with a mock module.
147
+
148
+ This Vite-specific choice exists for two reasons:
149
+
150
+ - avoid cross-environment cache contamination in shared resolver caches
151
+ - avoid cold-start export-resolution issues when importer AST data is not yet
152
+ available
153
+
154
+ ## Vite Virtual Modules
155
+
156
+ The shared mock generators use abstract ids like:
157
+
158
+ - `\0tanstack-start-import-protection:mock`
159
+ - `\0tanstack-start-import-protection:mock-edge:...`
160
+ - `\0tanstack-start-import-protection:mock-runtime:...`
161
+ - `\0tanstack-start-import-protection:marker:*`
162
+
163
+ The Vite adapter adds:
164
+
165
+ - resolved Vite ids via `resolveViteId(...)`
166
+ - support for browser-prefixed ids via `__x00__`
167
+ - `loadResolvedVirtualModule()` for serving the generated code
168
+
169
+ This id transport is adapter-specific. The generated module contents are shared.
170
+
171
+ ## Warm-Start Considerations
172
+
173
+ Vite can skip `resolveId` on warm cache hits, so the adapter maintains its own
174
+ graph and transform-result caches to recover enough information for traces and
175
+ pending-violation verification.
176
+
177
+ Special care is taken not to treat pre-transform lookup edges as authoritative
178
+ reachability edges.
179
+
180
+ ## Practical Maintainer Rule
181
+
182
+ When changing Vite import protection, ask:
183
+
184
+ 1. Is this really about Vite lifecycle timing or state?
185
+ 2. If not, should it move into the shared import-protection core?
186
+ 3. If yes, does it belong in resolve-time detection, transform-cache handling,
187
+ or bundle-time verification?