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,448 @@
1
+ import type { ServerResponse } from 'node:http'
2
+ import path from 'node:path'
3
+ import colors from 'picocolors'
4
+ import type { InputOptions, OutputOptions, Plugin } from 'rolldown'
5
+ import { build } from 'rolldown'
6
+ import { type DevEngine, dev, viteReporterPlugin } from 'rolldown/experimental'
7
+ import type { Connect, ViteDevServer } from 'vite'
8
+ import type { VitePluginTaroOptions } from '../../../../options.ts'
9
+ import { once } from '../../../utils/once.ts'
10
+ import { resolvePackageFile } from '../../../utils/packages.ts'
11
+ import { appShellFileName } from '../module.ts'
12
+ import {
13
+ type HmrInfo,
14
+ hmrControlPath,
15
+ hmrInfoFileName,
16
+ hmrPatchesFileName,
17
+ type PatchUpdate,
18
+ renderHmrInfo,
19
+ renderInitialHmrPatches,
20
+ writeHmrFile
21
+ } from './hmr-files.ts'
22
+ import { PatchPublisher } from './patch-publisher.ts'
23
+
24
+ export type WxDevHost = Readonly<{
25
+ close: () => Promise<void>
26
+ }>
27
+
28
+ type DeliveryReport = Readonly<{
29
+ kind: 'delivery'
30
+ buildId: string
31
+ seq: number
32
+ }>
33
+
34
+ /** The runtime hit an unrecoverable state (e.g. a corrupted patch range) and needs a full rebuild. */
35
+ type RebuildReport = Readonly<{
36
+ kind: 'rebuild'
37
+ buildId: string
38
+ }>
39
+
40
+ /**
41
+ * Creates the wx dev host: the adapter that owns the physical Rolldown DevEngine (created
42
+ * with dev(...)) and the patch publisher, and replaces Vite's bundledDev.listen so the
43
+ * engine writes directly to the Mini Program output directory instead of serving browser
44
+ * HMR over HTTP.
45
+ */
46
+ export async function createWxDevHost({
47
+ server,
48
+ options
49
+ }: {
50
+ server: ViteDevServer
51
+ options: VitePluginTaroOptions
52
+ }): Promise<WxDevHost> {
53
+ const bundledDev = getBundledDev(server)
54
+ // DevEngine does not reject run() after an initial plugin failure, so settle startup from its first buildEnd result.
55
+ // Later build errors belong to the running server and continue through onOutput/onHmrUpdates.
56
+ const initialBuild = Promise.withResolvers<void>()
57
+ const settleInitialBuild = once((error: Error | undefined): void => {
58
+ if (error) {
59
+ initialBuild.reject(error)
60
+ } else {
61
+ initialBuild.resolve()
62
+ }
63
+ })
64
+
65
+ const publisher = new PatchPublisher((content) =>
66
+ writeHmrFile(server.config.build.outDir, hmrPatchesFileName, content)
67
+ )
68
+
69
+ // Must install rolldown options before create engine
70
+ installRolldownOptions()
71
+ const engine: DevEngine = await createEngine()
72
+
73
+ // The wx dev host owns the only DevEngine. Vite's default listen() would create a second
74
+ // skip-write engine that renders into memory for browser HMR; instead the physical
75
+ // engine's initial build must finish before the HTTP server becomes ready, because
76
+ // DevTools opens the output directory directly and the app requires its files on disk.
77
+ bundledDev._devEngine = engine
78
+ bundledDev.triggerBundleRegenerationIfStale = async () => false
79
+ bundledDev.listen = async () => {
80
+ await Promise.all([engine.run(), initialBuild.promise])
81
+ await engine.ensureCurrentBuildFinish()
82
+ }
83
+
84
+ // Vite binds the port only after initServer (and therefore the initial build) completes, so
85
+ // the actual port is not observable while onOutput runs for the first build. The App metadata
86
+ // is written once the port is real; later full builds rewrite it from onOutput.
87
+ server.httpServer?.once('listening', startFreshBuild)
88
+
89
+ // The runtime's metadata-only reports land on the control path; the buildId in each report
90
+ // IS the Rolldown client ID.
91
+ server.middlewares.use(hmrControlPath, (req, res) => void handleReport(req, res))
92
+
93
+ // Append the DevTools project directory line after Vite's own startup banner: DevTools
94
+ // opens the output directory directly, so the printed path is the one to select.
95
+ const originalPrintUrls = server.printUrls.bind(server)
96
+ server.printUrls = () => {
97
+ originalPrintUrls()
98
+ server.config.logger.info(
99
+ ` ${colors.green('➜')} ${colors.bold('WeChat DevTools')}: ${colors.cyan(relativeToViteConfig(server.config.build.outDir, server.config.configFile, server.config.root))}`
100
+ )
101
+ }
102
+
103
+ return {
104
+ close: async () => {
105
+ await engine.close()
106
+ }
107
+ }
108
+
109
+ /** Rotates the build identity and materializes the App metadata for it. */
110
+ async function startFreshBuild(): Promise<void> {
111
+ const port = boundPort(server)
112
+ if (port === undefined) {
113
+ // The initial output finishes before Vite binds its port. The listening listener
114
+ // starts the first real client build, avoiding a phantom registered client.
115
+ return
116
+ }
117
+
118
+ const { buildId, previousBuildId } = publisher.startBuild()
119
+ if (previousBuildId) {
120
+ await engine.removeClient(previousBuildId)
121
+ }
122
+ await engine.registerClient(buildId)
123
+
124
+ // Publish info last: a heap that observes the new identity is guaranteed to see the
125
+ // matching reset patch file.
126
+ await writeHmrFile(server.config.build.outDir, hmrPatchesFileName, renderInitialHmrPatches())
127
+ await writeHmrInfo(server, buildId, port)
128
+ }
129
+
130
+ async function handleReport(req: Connect.IncomingMessage, res: ServerResponse): Promise<void> {
131
+ if (req.method !== 'POST') {
132
+ res.statusCode = 404
133
+ res.end()
134
+ return
135
+ }
136
+
137
+ try {
138
+ const report = JSON.parse(await readBody(req)) as DeliveryReport | RebuildReport
139
+ // Only the current build's reports can advance physical delivery;
140
+ // delayed reports from older builds are ignored so they can never influence the
141
+ // live build.
142
+ if (!publisher.isCurrentBuild(report.buildId)) {
143
+ res.end()
144
+ return
145
+ }
146
+ if (report.kind === 'rebuild') {
147
+ engine.triggerFullBuild()
148
+ res.end()
149
+ return
150
+ }
151
+ // Commit every newly acknowledged Rolldown payload to this client's ship map.
152
+ const deliveredFiles = publisher.acknowledge(report.seq)
153
+ await Promise.all(deliveredFiles.map((fileName) => engine.notifyPayloadDelivered(fileName)))
154
+ res.end()
155
+ } catch (e) {
156
+ logWxError(server.config.logger, 'wx HMR report failed', e)
157
+ res.statusCode = 400
158
+ res.end()
159
+ }
160
+ }
161
+
162
+ /** Creates the physical DevEngine with the wx dev host hooks. */
163
+ async function createEngine(): Promise<DevEngine> {
164
+ const rolldownOptions = await bundledDev.getRolldownOptions()
165
+ if (!rolldownOptions.output || Array.isArray(rolldownOptions.output)) {
166
+ throw new Error('wx development requires exactly one Rolldown output.')
167
+ }
168
+
169
+ return dev(rolldownOptions, rolldownOptions.output, {
170
+ onHmrUpdates: async (result) => {
171
+ if (result instanceof Error) {
172
+ logWxError(server.config.logger, 'wx HMR update failed', result)
173
+ return
174
+ }
175
+ // Collect the current client's batch first: one HMR event can carry several
176
+ // client envelopes, while only the active build may enter its patch history.
177
+ const batch: PatchUpdate[] = []
178
+ for (const { clientId, update } of result.updates) {
179
+ // Removed and delayed client sessions can still appear in a completed
180
+ // engine batch; they must never enter the current build's patch history.
181
+ if (!publisher.isCurrentBuild(clientId) || update.type === 'Noop') {
182
+ continue
183
+ }
184
+ if (update.type === 'Patch') {
185
+ batch.push(update)
186
+ continue
187
+ }
188
+
189
+ server.config.logger.info(
190
+ `[vpt] wx full rebuild required${update.reason ? `: ${update.reason}` : ''}`
191
+ )
192
+ engine.triggerFullBuild()
193
+ return
194
+ }
195
+ if (batch.length > 0) {
196
+ await publisher.produce(batch)
197
+ // server.config.logger.info('[vpt] wx patch produced')
198
+ }
199
+ },
200
+ onOutput: async (result) => {
201
+ if (result instanceof Error) {
202
+ logWxError(server.config.logger, 'wx dev build failed', result)
203
+ return
204
+ }
205
+ // A fresh build identity per complete physical build; the App runtime reads it
206
+ // from hmr/info.js before any module registers.
207
+ await startFreshBuild()
208
+ },
209
+ rebuildStrategy: 'never',
210
+ watch: { skipWrite: false }
211
+ })
212
+ }
213
+
214
+ /** Restores physical Mini Program output conventions after Vite applies browser bundled-dev defaults. */
215
+ function installRolldownOptions(): void {
216
+ const original = bundledDev.getRolldownOptions.bind(bundledDev)
217
+
218
+ bundledDev.getRolldownOptions = async () => {
219
+ const rolldownOptions = await original()
220
+ if (Array.isArray(rolldownOptions.output)) {
221
+ throw new Error('wx development requires one configured Rolldown output.')
222
+ }
223
+ rolldownOptions.output ??= {}
224
+ const output = rolldownOptions.output
225
+ const configuredOutput = server.config.build.rolldownOptions.output
226
+ if (Array.isArray(configuredOutput)) {
227
+ throw new Error('wx development supports one configured Rolldown output.')
228
+ }
229
+
230
+ // Every page entry must depend on hmr/patches.js: DevTools classifies a changed Page
231
+ // dependency as Page JavaScript hot reload and re-executes live Pages, which is the only
232
+ // trigger that delivers physical patches while keeping the App heap alive.
233
+ const pageFiles = new Set(options.pages.map((page) => `${page.path}.js`))
234
+
235
+ const configured = (configuredOutput ?? {}) as Record<string, unknown>
236
+ Object.assign(output, configured, {
237
+ assetFileNames: createStableFileNames(configured.assetFileNames, 'assets/[name][extname]'),
238
+ banner: createEntryBanner(pageFiles),
239
+ chunkFileNames: createStableFileNames(configured.chunkFileNames, 'assets/[name].js'),
240
+ entryFileNames: createStableFileNames(configured.entryFileNames, '[name]'),
241
+ format: 'es',
242
+ minify: true,
243
+ sourcemap: false
244
+ })
245
+
246
+ rolldownOptions.experimental ??= {}
247
+ rolldownOptions.experimental.devMode = {
248
+ ...(typeof rolldownOptions.experimental.devMode === 'object'
249
+ ? rolldownOptions.experimental.devMode
250
+ : {}),
251
+ implement: await bundleRuntimeSource(),
252
+ lazy: false
253
+ }
254
+
255
+ const reportInitialBuildPlugin: Plugin = {
256
+ name: 'vpt:wx-report-initial-build',
257
+ buildEnd: settleInitialBuild
258
+ }
259
+
260
+ rolldownOptions.plugins = [rolldownOptions.plugins, reportInitialBuildPlugin, createViteReporter(server)]
261
+ disableViteOxcSourcemap(rolldownOptions.plugins)
262
+ return rolldownOptions
263
+ }
264
+ }
265
+ }
266
+
267
+ /** The bound HTTP port, or undefined before Vite's server is listening. */
268
+ function boundPort(server: ViteDevServer): number | undefined {
269
+ const httpServer = server.httpServer
270
+ if (!httpServer) {
271
+ return undefined
272
+ }
273
+ const address = httpServer.address()
274
+ if (!address || typeof address === 'string') {
275
+ return undefined
276
+ }
277
+ return address.port
278
+ }
279
+
280
+ /** Writes the immutable App metadata every full build starts from. */
281
+ async function writeHmrInfo(server: ViteDevServer, buildId: string, port: number): Promise<void> {
282
+ const info: HmrInfo = {
283
+ buildId,
284
+ endpoint: `${server.config.server.https ? 'https' : 'http'}://${resolveEndpointHost(server)}:${port}${hmrControlPath}`
285
+ }
286
+
287
+ await writeHmrFile(server.config.build.outDir, hmrInfoFileName, renderHmrInfo(info))
288
+ }
289
+
290
+ /**
291
+ * The project directory shown in the DevTools banner, relative to the Vite config:
292
+ * `dist/wx` instead of the absolute output path, so it can be pasted into DevTools.
293
+ */
294
+ function relativeToViteConfig(outDir: string, configFile: string | undefined, root: string): string {
295
+ const configDirectory = configFile ? path.dirname(configFile) : root
296
+ const relativePath = path.relative(configDirectory, outDir).replaceAll('\\', '/')
297
+ if (!relativePath) return '.'
298
+ return relativePath.startsWith('.') ? relativePath : `./${relativePath}`
299
+ }
300
+
301
+ /** Logs an error with the plugin prefix, distinguishing Error values from unknowns. */
302
+ function logWxError(logger: ViteDevServer['config']['logger'], prefix: string, error: unknown): void {
303
+ if (Error.isError(error)) {
304
+ logger.error(`[vpt] ${prefix}`, { error })
305
+ } else {
306
+ logger.error(`[vpt] ${prefix} with unknown error: ${error}`)
307
+ }
308
+ }
309
+
310
+ // The runtime source is immutable for the host's lifetime (it changes only when the
311
+ // plugin is rebuilt), so the nested bundle runs once and every build reuses it.
312
+ const bundleRuntimeSource = once(
313
+ /** Bundles the runtime host and state machine into one plain script for injection. */
314
+ async function bundleRuntimeSource(): Promise<string> {
315
+ // write: false — only the code is consumed; without it rolldown drops the bundle into
316
+ // the default dist/ of the running project.
317
+ const result = await build({
318
+ input: resolvePackageFile('dist/runtime/wx/dev/dev-runtime.js'),
319
+ output: { format: 'iife', minify: true, sourcemap: false },
320
+ write: false
321
+ })
322
+ return result.output[0].code
323
+ }
324
+ )
325
+
326
+ const maximumBodyBytes = 64 * 1024
327
+
328
+ function readBody(req: Connect.IncomingMessage): Promise<string> {
329
+ return new Promise((resolve, reject) => {
330
+ let body = ''
331
+ req.on('data', (chunk: Buffer) => {
332
+ body += chunk.toString('utf8')
333
+ if (body.length > maximumBodyBytes) {
334
+ reject(new Error('report body too large'))
335
+ req.destroy()
336
+ }
337
+ })
338
+ req.on('end', () => resolve(body))
339
+ req.on('error', reject)
340
+ })
341
+ }
342
+
343
+ /** The bound address host, or loopback for wildcard binds; IPv6 literals are bracketed. */
344
+ function resolveEndpointHost(server: ViteDevServer): string {
345
+ const address = server.httpServer?.address()
346
+ if (!address || typeof address === 'string') {
347
+ return '127.0.0.1'
348
+ }
349
+ if (address.address === '0.0.0.0' || address.address === '::') {
350
+ // Wildcard binds accept loopback connections; 127.0.0.1 is the address DevTools can reach.
351
+ return '127.0.0.1'
352
+ }
353
+ return address.address.includes(':') ? `[${address.address}]` : address.address
354
+ }
355
+
356
+ /**
357
+ * Prepends entry banners. Banners are plain text appended after Rolldown's analysis, so the
358
+ * requires never become chunk dependencies (a bare require inside the injected runtime source
359
+ * would stall the build), and the wx render pipeline keeps this text after the hoisted chunk
360
+ * requires — so the runtime chunk exists before these run:
361
+ * - the app entry loads hmr/info.js and initializes the runtime before any module registers;
362
+ * - every page requires hmr/patches.js, the changed dependency that makes DevTools re-execute
363
+ * live Pages and thereby load physical updates.
364
+ */
365
+ function createEntryBanner(pageFiles: ReadonlySet<string>): (chunk: { name: string; fileName: string }) => string {
366
+ return (chunk) => {
367
+ if (chunk.name === appShellFileName) {
368
+ return "__rolldown_runtime__.initialize(require('./hmr/info.js'));\n"
369
+ }
370
+ if (pageFiles.has(chunk.name)) {
371
+ // Page files live at `pages/<route>/index.js`, so the dependency path must be
372
+ // computed relative to each page's own directory.
373
+ const patchesPath = path.posix.relative(path.posix.dirname(chunk.fileName), 'hmr/patches.js')
374
+ return `require('${patchesPath}');\n`
375
+ }
376
+ return ''
377
+ }
378
+ }
379
+
380
+ function createStableFileNames<Value>(addon: unknown, fallback: string): string | ((value: Value) => string) {
381
+ if (typeof addon === 'function') {
382
+ return (value) => toStableFileName(String(addon(value)))
383
+ }
384
+ return toStableFileName(typeof addon === 'string' ? addon : fallback)
385
+ }
386
+
387
+ function toStableFileName(fileName: string): string {
388
+ return fileName
389
+ .replace(/(^|\/)\[hash(?::\d+)?\](?=\.|$)/g, '$1[name]')
390
+ .replace(/[-_.]\[hash(?::\d+)?\]/g, '')
391
+ .replace(/\[hash(?::\d+)?\]/g, '[name]')
392
+ }
393
+
394
+ type ViteTransformPlugin = {
395
+ _options?: { transformOptions?: { sourcemap?: boolean } }
396
+ name?: string
397
+ }
398
+
399
+ function disableViteOxcSourcemap(pluginOption: unknown): void {
400
+ if (Array.isArray(pluginOption)) {
401
+ pluginOption.forEach(disableViteOxcSourcemap)
402
+ return
403
+ }
404
+ if (!pluginOption || typeof pluginOption !== 'object') {
405
+ return
406
+ }
407
+ const plugin = pluginOption as ViteTransformPlugin
408
+ if (plugin.name === 'builtin:vite-transform' && plugin._options?.transformOptions) {
409
+ plugin._options.transformOptions.sourcemap = false
410
+ }
411
+ }
412
+
413
+ function createViteReporter(server: ViteDevServer) {
414
+ const { build, logger, root } = server.config
415
+ return viteReporterPlugin({
416
+ assetsDir: path.join(build.assetsDir, '/'),
417
+ chunkLimit: 2000,
418
+ isLib: Boolean(build.lib),
419
+ isTty: Boolean(process.stdout.isTTY && !process.env.CI),
420
+ logInfo: (message) => logger.info(message),
421
+ reportCompressedSize: false,
422
+ root,
423
+ warnLargeChunks: false
424
+ })
425
+ }
426
+
427
+ type BundledDevRolldownOptions = InputOptions & {
428
+ experimental?: {
429
+ [key: string]: unknown
430
+ devMode?: boolean | Record<string, unknown>
431
+ }
432
+ output?: OutputOptions | OutputOptions[]
433
+ }
434
+
435
+ type BundledDev = {
436
+ _devEngine?: DevEngine
437
+ getRolldownOptions(): Promise<BundledDevRolldownOptions>
438
+ listen(): Promise<void>
439
+ triggerBundleRegenerationIfStale(): Promise<boolean>
440
+ }
441
+
442
+ function getBundledDev(server: ViteDevServer): BundledDev {
443
+ const bundledDev = server.environments.client.bundledDev as unknown as BundledDev | undefined
444
+ if (!bundledDev) {
445
+ throw new Error('Vite did not create the wx bundled-development environment.')
446
+ }
447
+ return bundledDev
448
+ }
@@ -0,0 +1,55 @@
1
+ import fs from 'node:fs/promises'
2
+ import path from 'node:path'
3
+ import type { BindingClientHmrUpdate } from 'rolldown/experimental'
4
+
5
+ export const hmrInfoFileName = 'hmr/info.js'
6
+ export const hmrPatchesFileName = 'hmr/patches.js'
7
+
8
+ /** Report endpoint path served by the wx dev control edge. */
9
+ export const hmrControlPath = '/__vpt_hmr__'
10
+
11
+ /** Immutable App metadata for the current complete physical build. */
12
+ export type HmrInfo = Readonly<{
13
+ buildId: string
14
+ endpoint: string
15
+ }>
16
+
17
+ /** The Patch variant of Rolldown's per-client HMR update, admitted into the patch history. */
18
+ export type PatchUpdate = Extract<BindingClientHmrUpdate['update'], { type: 'Patch' }>
19
+
20
+ /** Renders the synchronous CommonJS metadata module loaded by the runtime chunk. */
21
+ export function renderHmrInfo(info: HmrInfo): string {
22
+ return `module.exports = Object.freeze(${JSON.stringify(info)});\n`
23
+ }
24
+
25
+ /** Provides a valid dependency before the host has a patch range to publish. */
26
+ export function renderInitialHmrPatches(): string {
27
+ return 'module.exports = undefined;\n'
28
+ }
29
+
30
+ /**
31
+ * Renders the missing patch suffix as a passive physical delivery module.
32
+ *
33
+ * DevTools re-executes the Page because this file changed. The module only stores the literal
34
+ * Rolldown factories in the persistent App runtime; storePatches applies them synchronously,
35
+ * so the Page's imports below the require resolve against the freshly registered modules.
36
+ */
37
+ export function renderHmrPatches(buildId: string, patches: readonly PatchUpdate[]): string {
38
+ if (patches.length === 0) {
39
+ throw new Error('Cannot render an empty WX patch range.')
40
+ }
41
+
42
+ const rendered = patches.map(
43
+ (patch) =>
44
+ `{seq: ${patch.seq}, changedIds: ${JSON.stringify(patch.changedIds)}, factory: () => {\n${patch.code}\n}}`
45
+ )
46
+
47
+ return `__rolldown_runtime__.storePatches({buildId: ${JSON.stringify(buildId)}, patches: [${rendered.join(',')}]});\n`
48
+ }
49
+
50
+ /** Publishes one physical HMR file (direct write; atomic rename is reserved for later analysis). */
51
+ export async function writeHmrFile(outDir: string, fileName: string, source: string): Promise<void> {
52
+ const filePath = path.join(outDir, fileName)
53
+ await fs.mkdir(path.dirname(filePath), { recursive: true })
54
+ await fs.writeFile(filePath, source)
55
+ }
@@ -0,0 +1,49 @@
1
+ import { randomUUID } from 'node:crypto'
2
+ import { type PatchUpdate, renderHmrPatches } from './hmr-files.ts'
3
+
4
+ /** Abstracts the physical patches write; the engine owns the file destination. */
5
+ export type WritePatches = (content: string) => Promise<void>
6
+
7
+ /** Owns one build's unacknowledged patches and physical publish decision. */
8
+ export class PatchPublisher {
9
+ private readonly writePatches: WritePatches
10
+ private buildId: string | undefined
11
+
12
+ /** Executable updates retained only until the runtime acknowledges physical delivery. */
13
+ private readonly pendingPatches: PatchUpdate[] = []
14
+
15
+ // Explicit field assignment: node --test strips types and does not support parameter properties.
16
+ constructor(writePatches: WritePatches) {
17
+ this.writePatches = writePatches
18
+ }
19
+
20
+ /** True when the buildId belongs to the current full build. */
21
+ isCurrentBuild(buildId: string): boolean {
22
+ return buildId === this.buildId
23
+ }
24
+
25
+ /** Begins a fresh build and returns both sides of the client-session rotation. */
26
+ startBuild(): Readonly<{ buildId: string; previousBuildId: string | undefined }> {
27
+ const previousBuildId = this.buildId
28
+ const buildId = randomUUID()
29
+ this.buildId = buildId
30
+ this.pendingPatches.length = 0
31
+ return { buildId, previousBuildId }
32
+ }
33
+
34
+ /** Appends a Rolldown batch and publishes every unacknowledged patch. */
35
+ async produce(patches: readonly PatchUpdate[]): Promise<void> {
36
+ if (this.buildId === undefined || patches.length === 0) return
37
+ this.pendingPatches.push(...patches)
38
+ await this.writePatches(renderHmrPatches(this.buildId, this.pendingPatches))
39
+ }
40
+
41
+ /** Removes the pending prefix covered by the runtime's Rolldown sequence receipt. */
42
+ acknowledge(seq: number): string[] {
43
+ let deliveredCount = 0
44
+ while (deliveredCount < this.pendingPatches.length && this.pendingPatches[deliveredCount].seq <= seq) {
45
+ deliveredCount++
46
+ }
47
+ return this.pendingPatches.splice(0, deliveredCount).map((patch) => patch.filename)
48
+ }
49
+ }
@@ -0,0 +1,82 @@
1
+ import type { PluginOption } from 'vite'
2
+ import { transformWithOxc } from 'vite'
3
+ import type { VitePluginTaroOptions } from '../../../../options.ts'
4
+ import { esTarget } from '../../../utils/constant.ts'
5
+ import { rolldownRuntimeId } from '../module.ts'
6
+ import { memoize } from '../../../utils/memoize.ts'
7
+ import { createWxDevHost, type WxDevHost } from './dev-host.ts'
8
+ import { createWxReactRefreshTransforms } from './react-refresh.ts'
9
+
10
+ /**
11
+ * Adds the serve-only bundled-development plugin set for the wx target: the dev adapter
12
+ * (config, runtime lowering, dev host) plus the React Refresh adaptation transforms.
13
+ */
14
+ export function createWxDevelopmentPlugin(options: VitePluginTaroOptions): PluginOption[] {
15
+ let host: WxDevHost | null = null
16
+
17
+ return [
18
+ {
19
+ name: 'vpt:wx-dev',
20
+ apply: 'serve',
21
+
22
+ config() {
23
+ return {
24
+ build: {
25
+ // Disable maps in resolved environment config as well as final output so Oxc and Babel skip producing
26
+ // intermediate maps that Rolldown would discard.
27
+ sourcemap: false
28
+ },
29
+ experimental: {
30
+ // Ask Vite to resolve its bundled-development graph and expose the private adapter instance. The wx
31
+ // configureServer hook replaces only its startup method with the directly writing DevEngine.
32
+ bundledDev: true
33
+ }
34
+ }
35
+ },
36
+
37
+ transform: {
38
+ order: 'post',
39
+ // The dev-mode transform assembles the runtime chunk (Rolldown's base runtime
40
+ // plus our injected implement) as this module's transform output, which
41
+ // bypasses the build's es2018 lowering. Real-device engines and WeChat's
42
+ // upload parser predate class fields and nullish operators, so the assembled
43
+ // runtime is lowered here — the only module that needs it. The exact id
44
+ // filter needs no code scan; the id must stay in sync with rolldownRuntimeId
45
+ // in module.ts (kept as a regex for the Rolldown-side filter).
46
+ filter: { id: /^\0rolldown\/runtime\.js(?:\?|$)/ },
47
+ handler(code, id) {
48
+ // The `setPublicClassFields` assumption emits plain `this.x = ...`
49
+ // assignments instead of external helpers, whose references the later
50
+ // minifier would mangle.
51
+ return fixRolldownRuntime(code)
52
+ }
53
+ },
54
+
55
+ configureServer: {
56
+ // Install after Vite and user plugins have finished configuring the environment, but before server.listen()
57
+ // asks bundledDev to create its hard-coded skip-write DevEngine.
58
+ order: 'post',
59
+ async handler(server) {
60
+ host = await createWxDevHost({ server, options })
61
+ }
62
+ },
63
+
64
+ closeBundle() {
65
+ return host?.close()
66
+ }
67
+ },
68
+ ...createWxReactRefreshTransforms()
69
+ ]
70
+ }
71
+
72
+ // The assembled runtime chunk is byte-identical on every build (the base runtime and the
73
+ // bundled implement are immutable for the server's lifetime), so the lowering runs once
74
+ // and every build reuses it.
75
+ const fixRolldownRuntime = memoize((code: string) => {
76
+ return transformWithOxc(code, rolldownRuntimeId, {
77
+ lang: 'js',
78
+ target: esTarget,
79
+ sourcemap: false,
80
+ assumptions: { setPublicClassFields: true }
81
+ })
82
+ })