vite-plugin-taro 0.3.2 → 0.4.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 (260) hide show
  1. package/README.en.md +44 -0
  2. package/README.md +22 -497
  3. package/client.d.ts +9 -0
  4. package/dist/node/plugins/client/client-taro.d.ts +4 -0
  5. package/dist/node/plugins/client/client-taro.js +19 -0
  6. package/dist/node/plugins/client/constant.d.ts +4 -0
  7. package/dist/node/plugins/client/constant.js +4 -0
  8. package/dist/node/plugins/conditional/conditional-directives.d.ts +4 -0
  9. package/dist/node/plugins/{conditional-directives.js → conditional/conditional-directives.js} +27 -11
  10. package/dist/node/plugins/css/plugins.d.ts +4 -0
  11. package/dist/node/plugins/css/plugins.js +84 -0
  12. package/dist/node/plugins/h5/constant.d.ts +2 -0
  13. package/dist/node/plugins/h5/constant.js +3 -0
  14. package/dist/node/plugins/h5/plugins.d.ts +4 -0
  15. package/dist/node/plugins/h5/plugins.js +161 -0
  16. package/dist/node/plugins/h5/resolver/module-resolver.d.ts +14 -0
  17. package/dist/node/plugins/h5/resolver/module-resolver.js +19 -0
  18. package/dist/node/plugins/h5/transform-app.d.ts +10 -0
  19. package/dist/node/plugins/h5/transform-app.js +48 -0
  20. package/dist/node/plugins/wx/dev/dev-host.d.ts +15 -0
  21. package/dist/node/plugins/wx/dev/dev-host.js +352 -0
  22. package/dist/node/plugins/wx/dev/hmr-files.d.ts +28 -0
  23. package/dist/node/plugins/wx/dev/hmr-files.js +34 -0
  24. package/dist/node/plugins/wx/dev/patch-publisher.d.ts +22 -0
  25. package/dist/node/plugins/wx/dev/patch-publisher.js +40 -0
  26. package/dist/node/plugins/wx/dev/plugin.d.ts +7 -0
  27. package/dist/node/plugins/wx/dev/plugin.js +73 -0
  28. package/dist/node/plugins/wx/dev/react-refresh.d.ts +20 -0
  29. package/dist/node/plugins/wx/dev/react-refresh.js +198 -0
  30. package/dist/node/plugins/wx/module.d.ts +45 -0
  31. package/dist/node/plugins/wx/module.js +66 -0
  32. package/dist/node/plugins/wx/native/compile-native-component-interface.d.ts +21 -0
  33. package/dist/node/plugins/wx/native/compile-native-component-interface.js +29 -0
  34. package/dist/node/plugins/wx/native/create-native-component-output.d.ts +15 -0
  35. package/dist/node/plugins/wx/native/create-native-component-output.js +39 -0
  36. package/dist/node/plugins/wx/native/native-component-assets.d.ts +26 -0
  37. package/dist/node/plugins/wx/native/native-component-assets.js +76 -0
  38. package/dist/node/plugins/wx/native/native-component-interface.d.ts +12 -0
  39. package/dist/node/plugins/wx/native/native-component-interface.js +201 -0
  40. package/dist/node/plugins/wx/output/files.d.ts +12 -0
  41. package/dist/node/plugins/wx/output/files.js +12 -0
  42. package/dist/node/plugins/wx/output/json.d.ts +14 -0
  43. package/dist/node/plugins/wx/output/json.js +52 -0
  44. package/dist/node/plugins/wx/output/relative-root.d.ts +2 -0
  45. package/dist/node/plugins/wx/output/relative-root.js +6 -0
  46. package/dist/node/plugins/wx/output/templates.d.ts +7 -0
  47. package/dist/node/plugins/wx/output/templates.js +100 -0
  48. package/dist/node/plugins/wx/placement/placer.d.ts +78 -0
  49. package/dist/node/plugins/wx/placement/placer.js +145 -0
  50. package/dist/node/plugins/wx/placement/plan.d.ts +46 -0
  51. package/dist/node/plugins/wx/placement/plan.js +210 -0
  52. package/dist/node/plugins/wx/plugins.d.ts +4 -0
  53. package/dist/node/plugins/wx/plugins.js +137 -0
  54. package/dist/node/plugins/wx/render/capsule-wrapper.d.ts +3 -0
  55. package/dist/node/plugins/wx/render/capsule-wrapper.js +64 -0
  56. package/dist/node/plugins/wx/render/capsule.d.ts +4 -0
  57. package/dist/node/plugins/wx/render/capsule.js +13 -0
  58. package/dist/node/plugins/wx/render/native.d.ts +9 -0
  59. package/dist/node/plugins/wx/render/native.js +45 -0
  60. package/dist/node/plugins/wx/render/transport.d.ts +17 -0
  61. package/dist/node/plugins/wx/render/transport.js +94 -0
  62. package/dist/node/plugins/wx/resolve/resolver.d.ts +7 -0
  63. package/dist/node/plugins/wx/resolve/resolver.js +86 -0
  64. package/dist/node/plugins/wx/resolve/specialize-bootstrap.d.ts +9 -0
  65. package/dist/node/plugins/wx/resolve/specialize-bootstrap.js +9 -0
  66. package/dist/node/plugins/wx/resolve/specialize-page-capsule.d.ts +9 -0
  67. package/dist/node/plugins/wx/resolve/specialize-page-capsule.js +11 -0
  68. package/dist/node/utils/constant.d.ts +1 -0
  69. package/dist/node/utils/constant.js +1 -0
  70. package/dist/node/utils/memoize.d.ts +122 -0
  71. package/dist/node/utils/memoize.js +86 -0
  72. package/dist/node/utils/modules.d.ts +20 -24
  73. package/dist/node/utils/modules.js +21 -34
  74. package/dist/node/utils/once.d.ts +10 -0
  75. package/dist/node/utils/once.js +20 -0
  76. package/dist/node/utils/packages.d.ts +1 -0
  77. package/dist/node/utils/packages.js +1 -1
  78. package/dist/node/utils/project-config.d.ts +3 -0
  79. package/dist/node/utils/project-config.js +8 -0
  80. package/dist/node/utils/serialized-task-queue.d.ts +9 -0
  81. package/dist/node/utils/serialized-task-queue.js +17 -0
  82. package/dist/node/utils/transform.d.ts +11 -0
  83. package/dist/node/utils/transform.js +58 -0
  84. package/dist/node/vite-plugin.d.ts +1 -1
  85. package/dist/node/vite-plugin.js +11 -27
  86. package/dist/options.d.ts +3 -4
  87. package/dist/runtime/{taro → client/taro}/api.d.ts +0 -1
  88. package/dist/runtime/client/taro/api.js +9 -0
  89. package/dist/runtime/client/taro/component.d.ts +1 -0
  90. package/dist/runtime/client/taro/component.js +1 -0
  91. package/dist/runtime/client/taro/define-native-component.d.ts +7 -0
  92. package/dist/runtime/client/taro/define-native-component.js +4 -0
  93. package/dist/runtime/h5/app.d.ts +2 -0
  94. package/dist/runtime/h5/app.js +15 -0
  95. package/dist/runtime/h5/taro-runtime.d.ts +1 -1
  96. package/dist/runtime/h5/taro-runtime.js +1 -4
  97. package/dist/runtime/wx/amphibious/bootstrap.d.ts +4 -0
  98. package/dist/runtime/wx/amphibious/bootstrap.js +16 -0
  99. package/dist/runtime/wx/amphibious/transport.d.ts +2 -0
  100. package/dist/runtime/wx/amphibious/transport.js +4 -0
  101. package/dist/runtime/wx/capsule/app.d.ts +2 -0
  102. package/dist/runtime/wx/capsule/app.js +7 -0
  103. package/dist/runtime/wx/capsule/component.d.ts +3 -0
  104. package/dist/runtime/wx/capsule/component.js +4 -0
  105. package/dist/runtime/wx/capsule/page.d.ts +3 -0
  106. package/dist/runtime/wx/capsule/page.js +68 -0
  107. package/dist/runtime/wx/capsule/taro-runtime.d.ts +6 -0
  108. package/dist/runtime/wx/capsule/taro-runtime.js +6 -0
  109. package/dist/runtime/wx/dev/dev-runtime.d.ts +1 -0
  110. package/dist/runtime/wx/dev/dev-runtime.js +246 -0
  111. package/dist/runtime/wx/native/app.d.ts +1 -0
  112. package/dist/runtime/wx/native/app.js +3 -0
  113. package/dist/runtime/wx/native/component.d.ts +1 -0
  114. package/dist/runtime/wx/native/component.js +3 -0
  115. package/dist/runtime/wx/native/page.d.ts +1 -0
  116. package/dist/runtime/wx/native/page.js +4 -0
  117. package/dist/runtime/wx/systemjs/system-core.d.ts +4 -0
  118. package/dist/runtime/wx/systemjs/system-core.js +331 -0
  119. package/package.json +21 -14
  120. package/src/node/plugins/client/client-taro.ts +23 -0
  121. package/src/node/plugins/client/constant.ts +5 -0
  122. package/src/node/plugins/{conditional-directives.ts → conditional/conditional-directives.ts} +33 -13
  123. package/src/node/plugins/css/plugins.ts +96 -0
  124. package/src/node/plugins/h5/constant.ts +4 -0
  125. package/src/node/plugins/h5/plugins.ts +197 -0
  126. package/src/node/plugins/h5/resolver/module-resolver.ts +32 -0
  127. package/src/node/plugins/h5/transform-app.ts +90 -0
  128. package/src/node/plugins/wx/dev/dev-host.ts +448 -0
  129. package/src/node/plugins/wx/dev/hmr-files.ts +55 -0
  130. package/src/node/plugins/wx/dev/patch-publisher.ts +49 -0
  131. package/src/node/plugins/wx/dev/plugin.ts +82 -0
  132. package/src/node/plugins/wx/dev/react-refresh.ts +227 -0
  133. package/src/node/plugins/wx/module.ts +97 -0
  134. package/src/node/plugins/wx/native/compile-native-component-interface.ts +47 -0
  135. package/src/node/plugins/wx/native/create-native-component-output.ts +57 -0
  136. package/src/node/plugins/wx/native/native-component-assets.ts +114 -0
  137. package/src/node/plugins/wx/native/native-component-interface.ts +265 -0
  138. package/src/node/plugins/wx/output/files.ts +27 -0
  139. package/src/node/plugins/wx/output/json.ts +79 -0
  140. package/src/node/plugins/wx/output/relative-root.ts +7 -0
  141. package/src/node/plugins/wx/output/templates.ts +141 -0
  142. package/src/node/plugins/wx/placement/placer.ts +172 -0
  143. package/src/node/plugins/wx/placement/plan.ts +306 -0
  144. package/src/node/plugins/wx/plugins.ts +165 -0
  145. package/src/node/plugins/wx/render/capsule-wrapper.ts +86 -0
  146. package/src/node/plugins/wx/render/capsule.ts +21 -0
  147. package/src/node/plugins/wx/render/native.ts +84 -0
  148. package/src/node/plugins/wx/render/transport.ts +157 -0
  149. package/src/node/plugins/wx/resolve/resolver.ts +125 -0
  150. package/src/node/plugins/wx/resolve/specialize-bootstrap.ts +27 -0
  151. package/src/node/plugins/wx/resolve/specialize-page-capsule.ts +29 -0
  152. package/src/node/utils/constant.ts +1 -0
  153. package/src/node/utils/memoize.ts +149 -0
  154. package/src/node/utils/modules.ts +36 -38
  155. package/src/node/utils/once.ts +21 -0
  156. package/src/node/utils/packages.ts +1 -1
  157. package/src/node/utils/project-config.ts +10 -0
  158. package/src/node/utils/serialized-task-queue.ts +20 -0
  159. package/src/node/utils/transform.ts +82 -0
  160. package/src/node/vite-plugin.ts +12 -32
  161. package/src/options.ts +3 -4
  162. package/src/runtime/client/taro/api.ts +11 -0
  163. package/src/runtime/client/taro/component.ts +1 -0
  164. package/src/runtime/client/taro/define-native-component.ts +15 -0
  165. package/src/runtime/h5/app.ts +25 -0
  166. package/src/runtime/h5/taro-runtime.ts +1 -4
  167. package/src/runtime/wx/amphibious/bootstrap.ts +22 -0
  168. package/src/runtime/wx/amphibious/transport.ts +10 -0
  169. package/src/runtime/wx/capsule/app.ts +9 -0
  170. package/src/runtime/wx/capsule/component.ts +6 -0
  171. package/src/runtime/wx/capsule/page.ts +96 -0
  172. package/src/runtime/wx/capsule/taro-runtime.ts +14 -0
  173. package/src/runtime/wx/dev/dev-runtime.ts +317 -0
  174. package/src/runtime/wx/native/app.ts +4 -0
  175. package/src/runtime/wx/native/component.ts +4 -0
  176. package/src/runtime/wx/native/page.ts +6 -0
  177. package/src/runtime/wx/systemjs/LICENSE +10 -0
  178. package/src/runtime/wx/systemjs/system-core.js +370 -0
  179. package/src/runtime/wx/systemjs/system.d.ts +55 -0
  180. package/src/runtime/wx/wechat.d.ts +52 -0
  181. package/README.zh.md +0 -521
  182. package/dist/node/build-context.d.ts +0 -25
  183. package/dist/node/build-context.js +0 -47
  184. package/dist/node/css/css-pipeline.d.ts +0 -35
  185. package/dist/node/css/css-pipeline.js +0 -154
  186. package/dist/node/plugins/conditional-directives.d.ts +0 -3
  187. package/dist/node/plugins/taro-runtime.d.ts +0 -5
  188. package/dist/node/plugins/taro-runtime.js +0 -18
  189. package/dist/node/targets/h5/plugin.d.ts +0 -6
  190. package/dist/node/targets/h5/plugin.js +0 -117
  191. package/dist/node/targets/h5/virtual-modules.d.ts +0 -5
  192. package/dist/node/targets/h5/virtual-modules.js +0 -60
  193. package/dist/node/targets/wx/companion-assets.d.ts +0 -19
  194. package/dist/node/targets/wx/companion-assets.js +0 -129
  195. package/dist/node/targets/wx/dev-server/development-session.d.ts +0 -32
  196. package/dist/node/targets/wx/dev-server/development-session.js +0 -215
  197. package/dist/node/targets/wx/dev-server/full-build-scheduler.d.ts +0 -14
  198. package/dist/node/targets/wx/dev-server/full-build-scheduler.js +0 -60
  199. package/dist/node/targets/wx/dev-server/js-utils.d.ts +0 -5
  200. package/dist/node/targets/wx/dev-server/js-utils.js +0 -28
  201. package/dist/node/targets/wx/dev-server/output.d.ts +0 -19
  202. package/dist/node/targets/wx/dev-server/output.js +0 -44
  203. package/dist/node/targets/wx/dev-server/rolldown-runtime-source.d.ts +0 -8
  204. package/dist/node/targets/wx/dev-server/rolldown-runtime-source.js +0 -95
  205. package/dist/node/targets/wx/dev-server/update-server-state.d.ts +0 -65
  206. package/dist/node/targets/wx/dev-server/update-server-state.js +0 -116
  207. package/dist/node/targets/wx/dev-server/update-transport.d.ts +0 -28
  208. package/dist/node/targets/wx/dev-server/update-transport.js +0 -212
  209. package/dist/node/targets/wx/dev-server/vite-bundled-dev-adapter.d.ts +0 -44
  210. package/dist/node/targets/wx/dev-server/vite-bundled-dev-adapter.js +0 -145
  211. package/dist/node/targets/wx/development-files.d.ts +0 -8
  212. package/dist/node/targets/wx/development-files.js +0 -8
  213. package/dist/node/targets/wx/plugin.d.ts +0 -6
  214. package/dist/node/targets/wx/plugin.js +0 -144
  215. package/dist/node/targets/wx/react-refresh.d.ts +0 -4
  216. package/dist/node/targets/wx/react-refresh.js +0 -38
  217. package/dist/node/targets/wx/virtual-modules.d.ts +0 -16
  218. package/dist/node/targets/wx/virtual-modules.js +0 -120
  219. package/dist/node/utils/async.d.ts +0 -8
  220. package/dist/node/utils/async.js +0 -16
  221. package/dist/node/utils/filesystem.d.ts +0 -13
  222. package/dist/node/utils/filesystem.js +0 -43
  223. package/dist/runtime/taro/api.js +0 -11
  224. package/dist/runtime/taro/components.d.ts +0 -2
  225. package/dist/runtime/taro/components.js +0 -2
  226. package/dist/runtime/wx/page-update.d.ts +0 -6
  227. package/dist/runtime/wx/page-update.js +0 -157
  228. package/dist/runtime/wx/taro-runtime.d.ts +0 -5
  229. package/dist/runtime/wx/taro-runtime.js +0 -6
  230. package/dist/runtime/wx/update-client-state.d.ts +0 -68
  231. package/dist/runtime/wx/update-client-state.js +0 -84
  232. package/dist/runtime/wx/update-client.d.ts +0 -1
  233. package/dist/runtime/wx/update-client.js +0 -171
  234. package/src/node/build-context.ts +0 -62
  235. package/src/node/css/css-pipeline.ts +0 -171
  236. package/src/node/plugins/taro-runtime.ts +0 -22
  237. package/src/node/targets/h5/plugin.ts +0 -135
  238. package/src/node/targets/h5/virtual-modules.ts +0 -71
  239. package/src/node/targets/wx/companion-assets.ts +0 -174
  240. package/src/node/targets/wx/dev-server/development-session.ts +0 -271
  241. package/src/node/targets/wx/dev-server/full-build-scheduler.ts +0 -62
  242. package/src/node/targets/wx/dev-server/js-utils.ts +0 -33
  243. package/src/node/targets/wx/dev-server/output.ts +0 -56
  244. package/src/node/targets/wx/dev-server/rolldown-runtime-source.ts +0 -95
  245. package/src/node/targets/wx/dev-server/update-server-state.ts +0 -183
  246. package/src/node/targets/wx/dev-server/update-transport.ts +0 -261
  247. package/src/node/targets/wx/dev-server/vite-bundled-dev-adapter.ts +0 -203
  248. package/src/node/targets/wx/development-files.ts +0 -11
  249. package/src/node/targets/wx/plugin.ts +0 -164
  250. package/src/node/targets/wx/react-refresh.ts +0 -51
  251. package/src/node/targets/wx/virtual-modules.ts +0 -152
  252. package/src/node/utils/async.ts +0 -19
  253. package/src/node/utils/filesystem.ts +0 -45
  254. package/src/runtime/taro/api.ts +0 -13
  255. package/src/runtime/taro/components.ts +0 -2
  256. package/src/runtime/wx/page-update.ts +0 -230
  257. package/src/runtime/wx/react-refresh.d.ts +0 -20
  258. package/src/runtime/wx/taro-runtime.ts +0 -7
  259. package/src/runtime/wx/update-client-state.ts +0 -138
  260. package/src/runtime/wx/update-client.ts +0 -223
@@ -0,0 +1,227 @@
1
+ import { type PluginObject, types } from '@babel/core'
2
+ import type { Plugin } from 'vite'
3
+ import { memoize } from '../../../utils/memoize.ts'
4
+ import { transformWithBabel } from '../../../utils/transform.ts'
5
+
6
+ /** The React DevTools hook protocol name; free references must target `global` in wx. */
7
+ const reactDevtoolsHookProtocol = '__REACT_DEVTOOLS_GLOBAL_HOOK__'
8
+
9
+ /**
10
+ * Creates the serve-only React Refresh adaptation transforms for the wx target.
11
+ *
12
+ * @vitejs/plugin-react's generated refresh code assumes the web HTML preamble and a browser
13
+ * global scope; wx has neither. Each transform adapts one piece of that contract:
14
+ * - the refresh runtime module (id-filtered): the vendored runtime reads and assigns
15
+ * `window` protocol globals (rewritten to `global`) and must inject itself at evaluation
16
+ * — the preamble's `injectIntoGlobalHook` call has no HTML home in wx;
17
+ * - react-family modules (filtered on free references): the DevTools hook is read as a free
18
+ * variable, which the WeChat runtime scope never resolves against `global` — every free
19
+ * reference becomes an explicit member access;
20
+ * - boundary modules (filtered on the guard): the preamble's `$RefreshReg$` global is not
21
+ * needed because the transform generates local wrappers over the imported refresh
22
+ * runtime, so the guard that checks for the global is removed.
23
+ *
24
+ * Each transform's filter is its routing: the three domains are disjoint, so a module is
25
+ * transformed by at most one of them, and modules outside all three never reach a handler.
26
+ */
27
+ export function createWxReactRefreshTransforms(): Plugin[] {
28
+ return [
29
+ {
30
+ name: 'vpt:wx-react-refresh-runtime',
31
+ apply: 'serve',
32
+ transform: {
33
+ order: 'post',
34
+ // The vendored refresh runtime module; id-filtered, so no code scan.
35
+ filter: { id: /^\/@react-refresh(?:\?|$)/ },
36
+ handler(code, id) {
37
+ return fixRefreshRuntime({ code, id })
38
+ }
39
+ }
40
+ },
41
+ {
42
+ name: 'vpt:wx-react-devtools-hook',
43
+ apply: 'serve',
44
+ transform: {
45
+ order: 'post',
46
+ // Matches only free references (react-family modules). The member accesses in
47
+ // the refresh runtime and the dev runtime chunk never match, so they need no
48
+ // id exclusion.
49
+ filter: { code: /(^|[^.\w$])__REACT_DEVTOOLS_GLOBAL_HOOK__/ },
50
+ handler(code, id) {
51
+ return fixReactDevtoolsHook({ code, id })
52
+ }
53
+ }
54
+ },
55
+ {
56
+ name: 'vpt:wx-refresh-preamble-guard',
57
+ apply: 'serve',
58
+ transform: {
59
+ order: 'post',
60
+ // Vite's documented refresh protocol global; the generated guard is its only
61
+ // occurrence, in boundary modules.
62
+ filter: { code: /window\.\$RefreshReg\$/ },
63
+ handler(code, id) {
64
+ return transformWithBabel(code, id, [removeRefreshPreambleGuard], false)
65
+ }
66
+ }
67
+ }
68
+ ]
69
+ }
70
+
71
+ /**
72
+ * React-family modules: free `__REACT_DEVTOOLS_GLOBAL_HOOK__` reads must target `global`.
73
+ *
74
+ * The renderer checks the hook with `typeof __REACT_DEVTOOLS_GLOBAL_HOOK__` and injects via
75
+ * `hook.inject(...)` — but in the WeChat runtime, free-variable reads never resolve against
76
+ * `global`'s properties (verified: the free lookup is undefined while
77
+ * `global.__REACT_DEVTOOLS_GLOBAL_HOOK__` exists), so the renderer would silently skip
78
+ * injection and React Refresh would have no renderer to schedule re-renders on.
79
+ *
80
+ * The name is unique to the React DevTools protocol, so rewriting every free reference to an
81
+ * explicit member access is precise. The hook itself is created on `global` by the dev
82
+ * runtime chunk (see dev-runtime.ts).
83
+ */
84
+ function rewriteReactDevtoolsHookGlobal(): PluginObject {
85
+ return {
86
+ name: 'vpt:wx-react-devtools-hook-global',
87
+ visitor: {
88
+ Identifier(identifierPath) {
89
+ const node = identifierPath.node
90
+ if (!types.isIdentifier(node, { name: reactDevtoolsHookProtocol })) {
91
+ return
92
+ }
93
+ const parent = identifierPath.parentPath
94
+ // A member expression property (e.g. `global.__REACT_DEVTOOLS_GLOBAL_HOOK__`)
95
+ // is already explicit and must not be rewritten again.
96
+ if (parent.isMemberExpression() && parent.node.property === node) {
97
+ return
98
+ }
99
+ identifierPath.replaceWith(
100
+ types.memberExpression(types.identifier('global'), types.identifier(node.name))
101
+ )
102
+ }
103
+ }
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Refresh runtime module: self-inject at evaluation — the wx App heap has no HTML preamble.
109
+ *
110
+ * In web Vite, the HTML preamble calls `injectIntoGlobalHook(window)` before any module
111
+ * loads; nothing does that in wx. The call must live in this module itself:
112
+ * - its closure owns the refresh state (helpersByRendererID, mountedRoots), so the same
113
+ * instance that the generated boundary code imports must bootstrap itself;
114
+ * - the dev runtime chunk cannot reach it: the module is in a lazily-loaded chunk that
115
+ * requires the runtime chunk first, so it does not exist when the runtime evaluates.
116
+ *
117
+ * Unlike the preamble's `$RefreshReg$` globals (removed by removeRefreshPreambleGuard
118
+ * because boundary modules define local wrappers), the hook machinery has no local
119
+ * equivalent — without it the refresh runtime never learns the renderer or the mounted
120
+ * roots, so the injection is the one preamble responsibility that must be replicated.
121
+ *
122
+ * Timing is safe because injectIntoGlobalHook replays hook.renderers: the renderer already
123
+ * injected into the hook (created by the dev runtime chunk) when the App mounted, and the
124
+ * replay captures it; the patched commit hooks then track every later mount, including the
125
+ * remounts on re-execution.
126
+ */
127
+ function injectRefreshGlobalHook(): PluginObject {
128
+ return {
129
+ name: 'vpt:wx-refresh-global-hook-injection',
130
+ visitor: {
131
+ Program(programPath) {
132
+ // The renderer already injected into the hook (created by the dev runtime
133
+ // chunk) when the App mounted; injectIntoGlobalHook replays its renderers.
134
+ programPath.pushContainer(
135
+ 'body',
136
+ types.expressionStatement(
137
+ types.callExpression(types.identifier('injectIntoGlobalHook'), [types.identifier('global')])
138
+ )
139
+ )
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Refresh runtime module: rewrites the generated `window.<protocol>` accesses to `global`.
147
+ *
148
+ * The vendored refresh runtime is written for browsers and uses `window` for its protocol
149
+ * globals, but the WeChat runtime has no `window` — the free identifier is undefined — so the
150
+ * top-level assignment would throw at module evaluation, and the ignored-exports read
151
+ * inside the refresh validator would crash every update pass.
152
+ *
153
+ * Only the exact known protocol names are rewritten, one by one, so unrelated `window`
154
+ * accesses are never touched and future protocol additions are deliberate.
155
+ */
156
+ function rewriteRefreshRuntimeWindowAccess(): PluginObject {
157
+ /**
158
+ * Refresh protocol globals that must land on the WeChat `global`:
159
+ * - `__registerBeforePerformReactRefresh`: assigned at module scope; in web, the HMR
160
+ * client registers pre-refresh callbacks through it — the assignment throws on
161
+ * undefined `window`;
162
+ * - `__getReactRefreshIgnoredExports`: read in validateRefreshBoundaryAndEnqueueUpdate
163
+ * as an optional extension point — a read on undefined `window` crashes.
164
+ */
165
+ const refreshRuntimeWindowGlobals = ['__registerBeforePerformReactRefresh', '__getReactRefreshIgnoredExports']
166
+
167
+ return {
168
+ name: 'vpt:wx-refresh-runtime-window-access',
169
+ visitor: {
170
+ MemberExpression(memberPath) {
171
+ const member = memberPath.node
172
+ if (
173
+ !types.isIdentifier(member.object, { name: 'window' }) ||
174
+ !types.isIdentifier(member.property) ||
175
+ !refreshRuntimeWindowGlobals.includes(member.property.name)
176
+ ) {
177
+ return
178
+ }
179
+ // `global` is the WeChat global object — the same one the hook injection
180
+ // and the rest of the wx glue speak — so the protocol globals land on the
181
+ // App heap like any other global hook.
182
+ member.object = types.identifier('global')
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ /** Boundary modules: remove the generated `if (!window.$RefreshReg$) throw Error(...)` guard. */
189
+ function removeRefreshPreambleGuard(): PluginObject {
190
+ return {
191
+ name: 'vpt:wx-refresh-preamble-guard',
192
+ visitor: {
193
+ IfStatement(ifPath) {
194
+ // The guard is a web-only sanity check that the HTML preamble installed the
195
+ // `$RefreshReg$` global. wx has no preamble and no such global — but the
196
+ // boundary module does not need it: the plugin transform generates local
197
+ // `$RefreshReg$`/`$RefreshSig$` wrappers that delegate to the imported
198
+ // refresh runtime, so registration works without the global. The guard
199
+ // itself only crashes on the undefined `window`, so it is removed.
200
+ const test = ifPath.node.test
201
+ if (
202
+ types.isUnaryExpression(test, { operator: '!' }) &&
203
+ types.isMemberExpression(test.argument) &&
204
+ types.isIdentifier(test.argument.object, { name: 'window' }) &&
205
+ types.isIdentifier(test.argument.property, { name: '$RefreshReg$' })
206
+ ) {
207
+ ifPath.remove()
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }
213
+
214
+ // The refresh runtime module and the react-family modules are immutable for the server's
215
+ // lifetime (they only change when the plugin is rebuilt), so their Babel transforms run once
216
+ // per module and every build reuses the output.
217
+ const fixRefreshRuntime = memoize(
218
+ ({ code, id }: { code: string; id: string }) =>
219
+ transformWithBabel(code, id, [rewriteRefreshRuntimeWindowAccess, injectRefreshGlobalHook], false),
220
+ { getCacheKey: ({ code }) => code }
221
+ )
222
+
223
+ const fixReactDevtoolsHook = memoize(
224
+ ({ code, id }: { code: string; id: string }) =>
225
+ transformWithBabel(code, id, [rewriteReactDevtoolsHookGlobal], false),
226
+ { getCacheKey: ({ code }) => code }
227
+ )
@@ -0,0 +1,97 @@
1
+ import path from 'node:path'
2
+ import type { Rolldown } from 'vite'
3
+ import { normalizeModuleId } from '../../utils/modules.ts'
4
+ import { packageRequire, resolvePackageFile } from '../../utils/packages.ts'
5
+
6
+ /** Identifies Rolldown's generated helper module independently of its unstable output filename. */
7
+ export const rolldownRuntimeId = '\0rolldown/runtime.js'
8
+
9
+ /** Resolves the direct React Reconciler dependency without assuming pnpm's layout. */
10
+ export const reactReconcilerRoot = normalizeModuleId(
11
+ path.dirname(packageRequire.resolve('react-reconciler/package.json'))
12
+ )
13
+
14
+ /** Identifies the amphibious bootstrap that initializes SystemJS and serves every native shell. */
15
+ export const bootstrapPath = resolvePackageFile('dist/runtime/wx/amphibious/bootstrap.js')
16
+
17
+ /** Identifies the native transport source materialized before Rolldown finalizes content hashes. */
18
+ export const transportPath = resolvePackageFile('dist/runtime/wx/amphibious/transport.js')
19
+
20
+ /** Redirects Vite's injected browser preload helper to the bootstrap identity loader. */
21
+ export const vitePreloadId = '\0vite/preload-helper.js'
22
+
23
+ /** Forces the native App shell entry to emit at WeChat's required root path. */
24
+ export const appShellFileName = 'app.js'
25
+
26
+ /** Identifies the synchronous native App shell source. */
27
+ export const appShellPath = resolvePackageFile('dist/runtime/wx/native/app.js')
28
+
29
+ /** Identifies the App capsule kept behind the native shell boundary. */
30
+ export const appCapsulePath = resolvePackageFile('dist/runtime/wx/capsule/app.js')
31
+
32
+ /** Forces Taro's recursive native Component entry to emit at its configured root path. */
33
+ export const componentShellFileName = 'comp.js'
34
+
35
+ /** Identifies the synchronous recursive Component shell source. */
36
+ export const componentShellPath = resolvePackageFile('dist/runtime/wx/native/component.js')
37
+
38
+ /** Identifies the recursive Component capsule kept behind the native shell boundary. */
39
+ export const componentCapsulePath = resolvePackageFile('dist/runtime/wx/capsule/component.js')
40
+
41
+ /** Resolves the configured Page component from its route-qualified capsule importer. */
42
+ export const pageComponentId = '\0vpt:page-component'
43
+
44
+ /** Gives every Page shell one private capsule target that can be resolved using its route. */
45
+ export const pageCapsuleId = '\0vpt:page-capsule'
46
+
47
+ /** Provides the Page capsule source specialized through a stable route query. */
48
+ export const pageCapsulePath = resolvePackageFile('dist/runtime/wx/capsule/page.js')
49
+
50
+ /** Identifies the reusable synchronous native Page shell source. */
51
+ export const pageShellPath = resolvePackageFile('dist/runtime/wx/native/page.js')
52
+
53
+ export type WxChunk = Rolldown.PreRenderedChunk | Rolldown.RenderedChunk
54
+
55
+ /** Build-graph role of an explicit native lifecycle entry. */
56
+ export type WxEntryRole = 'shell' | 'capsule'
57
+
58
+ /** Runtime domain in which one final wx JavaScript chunk executes. */
59
+ export type WxExecutionKind = 'native' | 'capsule' | 'amphibious'
60
+
61
+ // These fixed source identities describe entry roles independently from the final execution kind. A capsule entry may,
62
+ // for example, become amphibious when Rolldown coalesces its generated runtime into the same output chunk.
63
+ const shellModuleIds: ReadonlySet<string> = new Set([appShellPath, componentShellPath, pageShellPath])
64
+ const capsuleModuleIds: ReadonlySet<string> = new Set([appCapsulePath, componentCapsulePath, pageCapsulePath])
65
+ const amphibiousModuleIds: ReadonlySet<string> = new Set([bootstrapPath, rolldownRuntimeId])
66
+
67
+ /** Returns the one explicit lifecycle role owned by a chunk. */
68
+ export function getWxEntryRole(chunk: WxChunk): WxEntryRole | undefined {
69
+ const ownsShell = containsModule(chunk, shellModuleIds)
70
+ const ownsCapsule = containsModule(chunk, capsuleModuleIds)
71
+ if (ownsShell && ownsCapsule) {
72
+ throw new Error(`wx chunk mixes shell and capsule entries: ${chunk.moduleIds.join(', ')}`)
73
+ }
74
+ return ownsShell ? 'shell' : ownsCapsule ? 'capsule' : undefined
75
+ }
76
+
77
+ /** Tests whether a chunk contains the physical transport implementation. */
78
+ export function isTransportModule(chunk: WxChunk): boolean {
79
+ return chunk.moduleIds.includes(transportPath)
80
+ }
81
+
82
+ /**
83
+ * Classifies one final output chunk by execution domain. Entry role is deliberately independent: explicit capsule entries
84
+ * normally fall through to capsule rendering, but a capsule containing an amphibious runtime executes in both domains.
85
+ */
86
+ export function getWxExecutionKind(chunk: WxChunk): WxExecutionKind {
87
+ const entryRole = getWxEntryRole(chunk)
88
+ if (containsModule(chunk, amphibiousModuleIds)) {
89
+ return 'amphibious'
90
+ }
91
+ return entryRole === 'shell' || isTransportModule(chunk) ? 'native' : 'capsule'
92
+ }
93
+
94
+ /** Tests normalized chunk module IDs against one fixed identity set. */
95
+ function containsModule(chunk: WxChunk, moduleIds: ReadonlySet<string>): boolean {
96
+ return chunk.moduleIds.some((moduleId) => moduleIds.has(normalizeModuleId(moduleId)))
97
+ }
@@ -0,0 +1,47 @@
1
+ import path from 'node:path'
2
+ import { normalizeModuleId } from '../../../utils/modules.ts'
3
+ import { collectNativeComponentAssets, nativeComponentMetaKey } from './native-component-assets.ts'
4
+ import { transformNativeComponentInterfaces } from './native-component-interface.ts'
5
+
6
+ /** Compiles one JSX interface module and attaches its opaque native sources to Rolldown metadata. */
7
+ export async function compileNativeComponentInterface({
8
+ code,
9
+ id,
10
+ sourcemap,
11
+ addWatchFile
12
+ }: {
13
+ code: string
14
+ id: string
15
+ sourcemap: boolean
16
+ addWatchFile: (file: string) => void
17
+ }) {
18
+ const transformed = transformNativeComponentInterfaces(code, id, sourcemap)
19
+ const interfacePath = normalizeModuleId(id)
20
+
21
+ const nativeComponents = await Promise.all(
22
+ transformed.definitions.map((definition) => collectNativeComponentAssets(definition, interfacePath))
23
+ )
24
+
25
+ nativeComponents.forEach((source) => {
26
+ addWatchFile(source.folder)
27
+
28
+ source.assets.forEach((asset) => {
29
+ addWatchFile(path.join(source.folder, asset.relativePath))
30
+ })
31
+ })
32
+
33
+ const assetBytes = nativeComponents.reduce((sourceTotal, source) => {
34
+ return sourceTotal + source.assets.reduce((assetTotal, asset) => assetTotal + asset.byteLength, 0)
35
+ }, 0)
36
+
37
+ return {
38
+ code: transformed.code,
39
+ map: transformed.map,
40
+ meta: {
41
+ [nativeComponentMetaKey]: {
42
+ sources: nativeComponents,
43
+ assetBytes
44
+ }
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,57 @@
1
+ import { readFile } from 'node:fs/promises'
2
+ import path from 'node:path'
3
+ import type { Rolldown } from 'vite'
4
+ import { isGeneratedSubpackageFile } from '../placement/plan.ts'
5
+ import { getNativeComponentSources } from './native-component-assets.ts'
6
+
7
+ /** Creates opaque native files and their registrations from each surviving JSX interface module. */
8
+ export async function createNativeComponentOutput({
9
+ bundle,
10
+ getModuleInfo
11
+ }: {
12
+ bundle: Rolldown.OutputBundle
13
+ getModuleInfo: (moduleId: string) => { meta: Rolldown.CustomPluginOptions } | null
14
+ }) {
15
+ // Files and registrations accumulate in final chunk order for deterministic output.
16
+ const files: Rolldown.EmittedAsset[] = []
17
+
18
+ const registrations: {
19
+ name: string
20
+ componentPath: string
21
+ fields: readonly string[]
22
+ }[] = []
23
+
24
+ for (const output of Object.values(bundle)) {
25
+ if (output.type !== 'chunk') {
26
+ continue
27
+ }
28
+ const packageRoot = isGeneratedSubpackageFile(output.fileName)
29
+ ? path.posix.dirname(path.posix.dirname(output.fileName))
30
+ : undefined
31
+
32
+ for (const moduleId of output.moduleIds) {
33
+ const sources = getNativeComponentSources(getModuleInfo(moduleId)?.meta)
34
+ for (const source of sources) {
35
+ const name = path.posix.basename(source.folder)
36
+ const componentFolder = path.posix.join('components', name)
37
+ const outputFolder = packageRoot ? path.posix.join(packageRoot, componentFolder) : componentFolder
38
+
39
+ registrations.push({
40
+ name,
41
+ componentPath: `/${path.posix.join(outputFolder, source.entry)}`,
42
+ fields: source.fields
43
+ })
44
+
45
+ for (const asset of source.assets) {
46
+ files.push({
47
+ type: 'asset',
48
+ fileName: path.posix.join(outputFolder, asset.relativePath),
49
+ source: await readFile(path.join(source.folder, asset.relativePath))
50
+ })
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ return { files, registrations }
57
+ }
@@ -0,0 +1,114 @@
1
+ import { readdir, stat } from 'node:fs/promises'
2
+ import path from 'node:path'
3
+ import type { Rolldown } from 'vite'
4
+ import { normalizeModuleId } from '../../../utils/modules.ts'
5
+ import type { NativeComponentDefinition } from './native-component-interface.ts'
6
+
7
+ type NativeComponentAsset = {
8
+ relativePath: string
9
+ byteLength: number
10
+ }
11
+
12
+ export const nativeComponentMetaKey = 'vpt:native-components'
13
+
14
+ type NativeComponentSource = NativeComponentDefinition & {
15
+ assets: readonly NativeComponentAsset[]
16
+ }
17
+
18
+ type NativeComponentMetadata = {
19
+ sources: readonly NativeComponentSource[]
20
+ assetBytes: number
21
+ }
22
+
23
+ declare module 'rolldown' {
24
+ interface CustomPluginOptions {
25
+ [nativeComponentMetaKey]?: NativeComponentMetadata
26
+ }
27
+ }
28
+
29
+ /** Reads the native contribution to a transformed module's package weight. */
30
+ export function getNativeComponentAssetBytes(meta: Rolldown.CustomPluginOptions | undefined): number {
31
+ return meta?.[nativeComponentMetaKey]?.assetBytes ?? 0
32
+ }
33
+
34
+ /** Reads native sources attached to one transformed JSX interface module. */
35
+ export function getNativeComponentSources(meta: Rolldown.CustomPluginOptions | undefined) {
36
+ return meta?.[nativeComponentMetaKey]?.sources ?? []
37
+ }
38
+
39
+ /** Collects a native component folder as opaque, recursively ordered assets without copying its interface module. */
40
+ export async function collectNativeComponentAssets(
41
+ definition: NativeComponentDefinition,
42
+ interfacePath: string
43
+ ): Promise<NativeComponentSource> {
44
+ await requireNativeComponentDirectory(definition.folder)
45
+ await requireNativeComponentEntry(definition)
46
+
47
+ // This mutable accumulator is local to one traversal and avoids repeatedly copying growing asset arrays.
48
+ const assets: NativeComponentAsset[] = []
49
+ await collectDirectory(definition.folder, [], assets, normalizeModuleId(interfacePath))
50
+
51
+ return { ...definition, assets }
52
+ }
53
+
54
+ /** Rejects a missing or non-directory source before Rolldown starts its recursive traversal. */
55
+ async function requireNativeComponentDirectory(folder: string): Promise<void> {
56
+ try {
57
+ if ((await stat(folder)).isDirectory()) {
58
+ return
59
+ }
60
+ } catch (error) {
61
+ if (Error.isError(error) && 'code' in error && error.code === 'ENOENT') {
62
+ throw new Error(`Native component folder does not exist: ${folder}`, { cause: error })
63
+ }
64
+ throw error
65
+ }
66
+ throw new Error(`Native component source is not a directory: ${folder}`)
67
+ }
68
+
69
+ /** Rejects a missing or non-file JavaScript entry before native output registration. */
70
+ async function requireNativeComponentEntry(definition: NativeComponentDefinition): Promise<void> {
71
+ const entryPath = path.join(definition.folder, `${definition.entry}.js`)
72
+ try {
73
+ if ((await stat(entryPath)).isFile()) {
74
+ return
75
+ }
76
+ } catch (error) {
77
+ if (Error.isError(error) && 'code' in error && error.code === 'ENOENT') {
78
+ throw new Error(`Native component entry file does not exist: ${entryPath}`, { cause: error })
79
+ }
80
+ throw error
81
+ }
82
+ throw new Error(`Native component entry is not a file: ${entryPath}`)
83
+ }
84
+
85
+ /** Traverses entries lexically so output remains deterministic across filesystems. */
86
+ async function collectDirectory(
87
+ folder: string,
88
+ segments: readonly string[],
89
+ assets: NativeComponentAsset[],
90
+ interfacePath: string
91
+ ): Promise<void> {
92
+ const directory = path.join(folder, ...segments)
93
+ const entries = await readdir(directory, { withFileTypes: true })
94
+ entries.sort((left, right) => left.name.localeCompare(right.name))
95
+
96
+ for (const entry of entries) {
97
+ const entrySegments = [...segments, entry.name]
98
+ if (entry.isDirectory()) {
99
+ await collectDirectory(folder, entrySegments, assets, interfacePath)
100
+ continue
101
+ }
102
+ if (!entry.isFile()) {
103
+ continue
104
+ }
105
+ const sourcePath = path.join(folder, ...entrySegments)
106
+ if (normalizeModuleId(sourcePath) === interfacePath) {
107
+ continue
108
+ }
109
+ assets.push({
110
+ relativePath: entrySegments.join('/'),
111
+ byteLength: (await stat(sourcePath)).size
112
+ })
113
+ }
114
+ }