vite-plugin-taro 0.5.4 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/dist/node/plugins/h5/create-stencil-client-adapter.d.ts +10 -13
- package/dist/node/plugins/h5/create-stencil-client-adapter.js +63 -51
- package/dist/node/plugins/h5/plugins.d.ts +10 -0
- package/dist/node/plugins/h5/plugins.js +39 -11
- package/dist/node/plugins/wx/dev/dev-host.js +103 -216
- package/dist/node/plugins/wx/dev/hmr-files.d.ts +4 -5
- package/dist/node/plugins/wx/dev/hmr-files.js +18 -8
- package/dist/node/plugins/wx/dev/patch-publisher.d.ts +25 -5
- package/dist/node/plugins/wx/dev/patch-publisher.js +30 -9
- package/dist/node/plugins/wx/dev/react-refresh.d.ts +12 -0
- package/dist/node/plugins/wx/dev/react-refresh.js +111 -108
- package/dist/node/plugins/wx/dev/wx-dev-options.d.ts +25 -0
- package/dist/node/plugins/wx/dev/wx-dev-options.js +147 -0
- package/dist/node/utils/oxc-transform.d.ts +21 -0
- package/dist/node/utils/oxc-transform.js +58 -0
- package/dist/node/utils/serialized-task-queue.d.ts +6 -2
- package/dist/node/utils/serialized-task-queue.js +10 -2
- package/dist/runtime/wx/capsule/page.js +14 -5
- package/dist/runtime/wx/dev/dev-runtime.js +70 -26
- package/package.json +4 -3
- package/src/node/plugins/h5/create-stencil-client-adapter.ts +74 -73
- package/src/node/plugins/h5/plugins.ts +42 -13
- package/src/node/plugins/wx/dev/dev-host.ts +121 -256
- package/src/node/plugins/wx/dev/hmr-files.ts +20 -8
- package/src/node/plugins/wx/dev/patch-publisher.ts +36 -10
- package/src/node/plugins/wx/dev/react-refresh.ts +125 -129
- package/src/node/plugins/wx/dev/wx-dev-options.ts +200 -0
- package/src/node/utils/oxc-transform.ts +77 -0
- package/src/node/utils/serialized-task-queue.ts +15 -3
- package/src/runtime/wx/capsule/page.ts +23 -9
- package/src/runtime/wx/dev/dev-runtime.ts +80 -28
|
@@ -26,18 +26,23 @@ function forward(handler: unknown, receiver: unknown, args: unknown[]): void {
|
|
|
26
26
|
;(handler as LifecycleHandler | undefined)?.apply(receiver, args)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
/** The
|
|
30
|
-
type
|
|
29
|
+
/** The old native projection retained while DevTools replaces its Page instance. */
|
|
30
|
+
type PageSnapshot = {
|
|
31
31
|
$taroPath: string
|
|
32
32
|
$taroParams: Record<string, unknown>
|
|
33
|
+
data: Record<string, unknown>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type NativePage = PageSnapshot & {
|
|
37
|
+
setData(data: Record<string, unknown>): void
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
// DevTools re-executes the page and replays the replacement lifecycle on every edit. Taro's
|
|
36
41
|
// onUnload unmounts the React tree and onLoad mounts a fresh one, destroying state. While
|
|
37
42
|
// the runtime says a patch was just delivered, the pair is intercepted instead: onUnload
|
|
38
|
-
// captures the surviving page identity
|
|
39
|
-
//
|
|
40
|
-
// capsule module is cached across re-executions, so this closure holds the capture.
|
|
43
|
+
// captures the surviving page identity and its current native render data, and onLoad
|
|
44
|
+
// immediately paints that snapshot before rebinding and fully synchronizing the retained
|
|
45
|
+
// tree. The capsule module is cached across re-executions, so this closure holds the capture.
|
|
41
46
|
// Ordinary navigation (no patch) passes through unchanged. The wrappers are regular
|
|
42
47
|
// functions so the native page instance (`this`) reaches the original handlers.
|
|
43
48
|
if (typeof __rolldown_runtime__ !== 'undefined') {
|
|
@@ -45,14 +50,19 @@ if (typeof __rolldown_runtime__ !== 'undefined') {
|
|
|
45
50
|
const originalOnLoad = config.onLoad
|
|
46
51
|
const originalOnShow = config.onShow
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
// This is the sole cross-instance handoff. It is mutable because DevTools destroys the
|
|
54
|
+
// old native Page before creating its replacement; ordinary navigation never reads it.
|
|
55
|
+
let captured: PageSnapshot | undefined
|
|
49
56
|
|
|
50
57
|
config.onUnload = function (this: unknown, ...args: unknown[]) {
|
|
51
58
|
if (__rolldown_runtime__.isHotReloading()) {
|
|
52
|
-
const instance = this as unknown as
|
|
59
|
+
const instance = this as unknown as NativePage
|
|
53
60
|
captured = {
|
|
54
61
|
$taroPath: instance.$taroPath,
|
|
55
|
-
$taroParams: instance.$taroParams
|
|
62
|
+
$taroParams: instance.$taroParams,
|
|
63
|
+
// WeChat owns this serializable view-model snapshot. Retaining it is cheaper
|
|
64
|
+
// than deep-cloning the complete recursive Taro node tree before every edit.
|
|
65
|
+
data: instance.data
|
|
56
66
|
}
|
|
57
67
|
return
|
|
58
68
|
}
|
|
@@ -60,10 +70,14 @@ if (typeof __rolldown_runtime__ !== 'undefined') {
|
|
|
60
70
|
}
|
|
61
71
|
config.onLoad = function (this: unknown, ...args: unknown[]) {
|
|
62
72
|
if (__rolldown_runtime__.isHotReloading() && captured) {
|
|
73
|
+
const instance = this as unknown as NativePage
|
|
74
|
+
// Paint the last native projection as the first bridge operation. Taro's full
|
|
75
|
+
// performUpdate below runs in a timer, so this direct setData removes most of the
|
|
76
|
+
// empty-page interval without delaying synchronization to the current tree.
|
|
77
|
+
instance.setData(captured.data)
|
|
63
78
|
// Restore the surviving page identity onto the replacement native instance: the
|
|
64
79
|
// re-loaded $taroPath embeds a fresh timestamp, so without this the tree and the
|
|
65
80
|
// native side would disagree on the page key.
|
|
66
|
-
const instance = this as unknown as PageIdentity
|
|
67
81
|
instance.$taroPath = captured.$taroPath
|
|
68
82
|
instance.$taroParams = captured.$taroParams
|
|
69
83
|
// Rebind the retained tree to the replacement receiver and resync the native
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
// The `DevRuntime` base class is injected into the chunk by Rolldown's dev-mode
|
|
6
6
|
// transform, so the WX host only extends it.
|
|
7
7
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// sequences are ignored when another Page requires the same physical file.
|
|
8
|
+
// Every Page explicitly passes the inert hmr/patches.js export here before importing its capsule. The runtime applies that
|
|
9
|
+
// cumulative suffix synchronously, reports its successful application frontier, and ignores sequences replayed by other Pages.
|
|
11
10
|
|
|
12
11
|
import type { DevRuntime as RolldownDevRuntime } from 'rolldown/experimental/runtime-types'
|
|
13
12
|
|
|
@@ -110,7 +109,7 @@ class WxDevRuntime extends DevRuntime {
|
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
/** Computes accepting boundaries and every executed module that must be re-armed. */
|
|
113
|
-
private computeHmrUpdate(changedIds:
|
|
112
|
+
private computeHmrUpdate(changedIds: Iterable<string>): HmrUpdate | undefined {
|
|
114
113
|
// Mutable traversal accumulators are confined to one synchronous update plan.
|
|
115
114
|
const boundaries: string[] = []
|
|
116
115
|
const updateSet = new Set<string>()
|
|
@@ -166,7 +165,7 @@ class WxDevRuntime extends DevRuntime {
|
|
|
166
165
|
* Applies one patch in three phases: capture old contexts, evict the whole update set,
|
|
167
166
|
* then re-run accepting modules and pass their fresh exports to the old contexts.
|
|
168
167
|
*/
|
|
169
|
-
private applyHmrUpdate(changedIds:
|
|
168
|
+
private applyHmrUpdate(changedIds: Iterable<string>): void {
|
|
170
169
|
const update = this.computeHmrUpdate(changedIds)
|
|
171
170
|
if (!update) return
|
|
172
171
|
|
|
@@ -195,14 +194,12 @@ class WxDevRuntime extends DevRuntime {
|
|
|
195
194
|
}
|
|
196
195
|
}
|
|
197
196
|
|
|
198
|
-
/** Consumed once per App heap from hmr/info.js; the host buildId
|
|
197
|
+
/** Consumed once per App heap from hmr/info.js; the host buildId identifies its cumulative patch history. */
|
|
199
198
|
initialize(info: HmrInfo): void {
|
|
200
199
|
if (this.hmrInfo) {
|
|
201
200
|
return
|
|
202
201
|
}
|
|
203
202
|
this.hmrInfo = info
|
|
204
|
-
// Anchor: the host publishes only after the runtime reports its delivery position.
|
|
205
|
-
void this.sendReport({ kind: 'delivery', seq: this.appliedSeq })
|
|
206
203
|
}
|
|
207
204
|
|
|
208
205
|
/** True between a patch delivery and the next page show: a hot reload is in progress. */
|
|
@@ -218,17 +215,17 @@ class WxDevRuntime extends DevRuntime {
|
|
|
218
215
|
this.hotReloading = false
|
|
219
216
|
}
|
|
220
217
|
|
|
221
|
-
/**
|
|
222
|
-
|
|
218
|
+
/** Applies one Page-delivered payload before that Page imports its capsule. */
|
|
219
|
+
applyPatches(payload: PatchPayload | undefined): void {
|
|
220
|
+
// The initial physical dependency exports undefined until the host has a patch range.
|
|
221
|
+
if (!payload) return
|
|
222
|
+
|
|
223
223
|
const info = this.hmrInfo
|
|
224
224
|
if (!info || payload.buildId !== info.buildId) {
|
|
225
225
|
console.warn('[vpt] patches for a stale build')
|
|
226
226
|
return
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
const deliveredSeq = Math.max(this.appliedSeq, payload.patches.at(-1)?.seq ?? 0)
|
|
230
|
-
void this.sendReport({ kind: 'delivery', seq: deliveredSeq })
|
|
231
|
-
|
|
232
229
|
// A delivered patch means DevTools is about to replay the page lifecycle on the
|
|
233
230
|
// re-executing Pages; the capsule wrapper suppresses the synthetic unmount/mount so
|
|
234
231
|
// the React tree survives and Refresh swaps the code in place.
|
|
@@ -236,27 +233,82 @@ class WxDevRuntime extends DevRuntime {
|
|
|
236
233
|
|
|
237
234
|
// Apply synchronously: the page's imports below the require resolve against the
|
|
238
235
|
// freshly registered modules, so the re-executed Page evaluates with the new code.
|
|
239
|
-
this.
|
|
236
|
+
if (this.applyPatchBatch(payload.patches)) {
|
|
237
|
+
// The host may publish later generations while this synchronous apply runs. Reporting only afterward makes this
|
|
238
|
+
// the application frontier: publisher history is never pruned merely because its JavaScript file was observed.
|
|
239
|
+
void this.sendReport({ kind: 'applied', seq: this.appliedSeq })
|
|
240
|
+
}
|
|
240
241
|
}
|
|
241
242
|
|
|
242
|
-
/**
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Folds one cumulative physical patch file into a single logical HMR update.
|
|
245
|
+
*
|
|
246
|
+
* `hmr/patches.js` can contain several unacknowledged Rolldown patches and can be required by several Page shells. The
|
|
247
|
+
* runtime must therefore ignore replayed sequences, reject a missing sequence, and move directly from the currently live
|
|
248
|
+
* module generation to the latest published generation without rendering intermediate generations.
|
|
249
|
+
*
|
|
250
|
+
* For an appliedSeq of 4:
|
|
251
|
+
*
|
|
252
|
+
* - [5, 6, 7] registers every factory, performs one update using their unioned changedIds, commits appliedSeq 7, and reports 7;
|
|
253
|
+
* - a second Page evaluating [5, 6, 7] skips the complete replay and leaves the latest factories untouched;
|
|
254
|
+
* - [5, 7] fails at the expected sequence 6, keeps appliedSeq 4, and requests a full rebuild because factory 6 is unrecoverable.
|
|
255
|
+
*/
|
|
256
|
+
private applyPatchBatch(patches: readonly PatchProgram[]): boolean {
|
|
257
|
+
// Keep the initial watermark immutable throughout the fold. Comparing replays against a moving watermark would allow
|
|
258
|
+
// a duplicate new sequence in the same payload to masquerade as an already-applied patch.
|
|
259
|
+
const previousSeq = this.appliedSeq
|
|
260
|
+
|
|
261
|
+
// Mutable only during this synchronous pass. `nextSeq` validates continuity while `changedIds` unions every incremental
|
|
262
|
+
// patch's roots for the one final graph traversal; neither value escapes into persistent runtime state.
|
|
263
|
+
let nextSeq = previousSeq + 1
|
|
264
|
+
const changedIds = new Set<string>()
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
for (const patch of patches) {
|
|
268
|
+
// The same physical file is evaluated once per affected Page. Those later evaluations replay its prefix and
|
|
269
|
+
// must not let an old factory overwrite the newer factory already stored in the Rolldown runtime.
|
|
270
|
+
if (patch.seq <= previousSeq) {
|
|
271
|
+
continue
|
|
272
|
+
}
|
|
246
273
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (patch.seq !==
|
|
250
|
-
throw new Error(`missing patch sequence ${
|
|
274
|
+
// Every patch is incremental: if one physical write was missed, later factories cannot reconstruct modules
|
|
275
|
+
// changed only by that missing patch. Rebuilding is safer than silently running a mixed module generation.
|
|
276
|
+
if (patch.seq !== nextSeq) {
|
|
277
|
+
throw new Error(`missing patch sequence ${nextSeq}`)
|
|
251
278
|
}
|
|
279
|
+
|
|
280
|
+
// A patch factory only registers module graphs and module factories; it does not execute application modules.
|
|
281
|
+
// Running all of them first leaves the registry at the latest generation while avoiding intermediate renders.
|
|
252
282
|
patch.factory()
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
283
|
+
|
|
284
|
+
for (const changedId of patch.changedIds) {
|
|
285
|
+
changedIds.add(changedId)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
nextSeq++
|
|
259
289
|
}
|
|
290
|
+
|
|
291
|
+
// A payload containing only replayed sequences requires no graph work and must not alter the application watermark.
|
|
292
|
+
if (nextSeq === previousSeq + 1) {
|
|
293
|
+
return true
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Apply once after every factory is registered. React Refresh arms a newly executed module's hot.accept callback
|
|
297
|
+
// in a microtask; applying consecutive patches separately would inspect that intermediate context too early and
|
|
298
|
+
// incorrectly bubble through the capsule to the native Page shell as though no HMR boundary existed.
|
|
299
|
+
this.applyHmrUpdate(changedIds)
|
|
300
|
+
|
|
301
|
+
// Commit application only after graph propagation and boundary callbacks succeed. A failure leaves the old watermark
|
|
302
|
+
// intact and requests a full rebuild below, so a partially applied batch is never acknowledged as healthy.
|
|
303
|
+
this.appliedSeq = nextSeq - 1
|
|
304
|
+
return true
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.warn('[vpt] patch batch failed; apply stopped', error)
|
|
307
|
+
void this.sendReport({
|
|
308
|
+
kind: 'rebuild',
|
|
309
|
+
reason: Error.isError(error) ? error.message : String(error)
|
|
310
|
+
})
|
|
311
|
+
return false
|
|
260
312
|
}
|
|
261
313
|
}
|
|
262
314
|
|