vite-plugin-taro 0.5.12 → 0.5.14
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/dist/node/plugins/client/client-taro.d.ts +1 -1
- package/dist/node/plugins/client/client-taro.js +2 -1
- package/dist/node/plugins/h5/create-stencil-client-adapter.d.ts +1 -1
- package/dist/node/plugins/h5/create-stencil-client-adapter.js +2 -1
- package/dist/node/plugins/h5/plugins.js +22 -1
- package/dist/node/plugins/h5/resolver/module-resolver.js +2 -1
- package/dist/node/plugins/tailwind/tailwind-css.d.ts +1 -0
- package/dist/node/plugins/tailwind/tailwind-css.js +3 -0
- package/dist/node/plugins/wx/dev/create-hmr-results-stream.d.ts +23 -0
- package/dist/node/plugins/wx/dev/create-hmr-results-stream.js +57 -0
- package/dist/node/plugins/wx/dev/create-style-capture.d.ts +42 -0
- package/dist/node/plugins/wx/dev/create-style-capture.js +125 -0
- package/dist/node/plugins/wx/dev/dev-host.d.ts +5 -1
- package/dist/node/plugins/wx/dev/dev-host.js +170 -47
- package/dist/node/plugins/wx/dev/hmr-files.d.ts +10 -0
- package/dist/node/plugins/wx/dev/hmr-files.js +12 -0
- package/dist/node/plugins/wx/dev/host-actions.d.ts +16 -0
- package/dist/node/plugins/wx/dev/host-actions.js +58 -0
- package/dist/node/plugins/wx/dev/patch-publisher.d.ts +2 -2
- package/dist/node/plugins/wx/dev/patch-publisher.js +17 -1
- package/dist/node/plugins/wx/dev/plugins.d.ts +25 -2
- package/dist/node/plugins/wx/dev/plugins.js +61 -9
- package/dist/node/plugins/wx/dev/react-refresh.js +6 -2
- package/dist/node/plugins/wx/dev/runtime-reports.d.ts +27 -0
- package/dist/node/plugins/wx/dev/runtime-reports.js +45 -0
- package/dist/node/plugins/wx/dev/wx-dev-options.d.ts +10 -5
- package/dist/node/plugins/wx/dev/wx-dev-options.js +47 -24
- package/dist/node/plugins/wx/module.d.ts +1 -1
- package/dist/node/plugins/wx/module.js +2 -1
- package/dist/node/plugins/wx/native/compile-native-component-interface.js +2 -1
- package/dist/node/plugins/wx/native/create-native-component-output.js +2 -0
- package/dist/node/plugins/wx/native/native-component-assets.d.ts +1 -1
- package/dist/node/plugins/wx/native/native-component-assets.js +3 -3
- package/dist/node/plugins/wx/native/native-component-interface.js +2 -1
- package/dist/node/plugins/wx/output/files.d.ts +1 -1
- package/dist/node/plugins/wx/output/files.js +6 -1
- package/dist/node/plugins/wx/plugins.js +13 -6
- package/dist/node/plugins/wx/resolve/resolver.d.ts +2 -1
- package/dist/node/plugins/wx/resolve/resolver.js +34 -19
- package/dist/node/plugins/wx/styles/plugins.d.ts +3 -0
- package/dist/node/plugins/wx/styles/plugins.js +111 -0
- package/dist/node/plugins/wx/styles/transform-wx-style.d.ts +8 -0
- package/dist/node/plugins/wx/styles/transform-wx-style.js +9 -0
- package/dist/node/plugins/wx/styles/utils.d.ts +39 -0
- package/dist/node/plugins/wx/styles/utils.js +95 -0
- package/dist/node/utils/modules.d.ts +1 -1
- package/dist/node/utils/modules.js +4 -3
- package/dist/node/utils/packages.js +2 -2
- package/dist/node/utils/vite.d.ts +17 -0
- package/dist/node/utils/vite.js +43 -0
- package/dist/node/vite-plugin.js +0 -2
- package/package.json +4 -3
- package/src/node/plugins/client/client-taro.ts +2 -2
- package/src/node/plugins/h5/create-stencil-client-adapter.ts +2 -2
- package/src/node/plugins/h5/plugins.ts +22 -1
- package/src/node/plugins/h5/resolver/module-resolver.ts +2 -1
- package/src/node/plugins/tailwind/tailwind-css.ts +4 -0
- package/src/node/plugins/wx/dev/create-hmr-results-stream.ts +71 -0
- package/src/node/plugins/wx/dev/create-style-capture.ts +179 -0
- package/src/node/plugins/wx/dev/dev-host.ts +198 -63
- package/src/node/plugins/wx/dev/hmr-files.ts +13 -0
- package/src/node/plugins/wx/dev/host-actions.ts +88 -0
- package/src/node/plugins/wx/dev/patch-publisher.ts +19 -2
- package/src/node/plugins/wx/dev/plugins.ts +67 -10
- package/src/node/plugins/wx/dev/react-refresh.ts +6 -2
- package/src/node/plugins/wx/dev/runtime-reports.ts +70 -0
- package/src/node/plugins/wx/dev/wx-dev-options.ts +53 -32
- package/src/node/plugins/wx/module.ts +2 -4
- package/src/node/plugins/wx/native/compile-native-component-interface.ts +2 -1
- package/src/node/plugins/wx/native/create-native-component-output.ts +2 -0
- package/src/node/plugins/wx/native/native-component-assets.ts +3 -4
- package/src/node/plugins/wx/native/native-component-interface.ts +2 -1
- package/src/node/plugins/wx/output/files.ts +6 -1
- package/src/node/plugins/wx/plugins.ts +16 -6
- package/src/node/plugins/wx/resolve/resolver.ts +36 -19
- package/src/node/plugins/wx/styles/plugins.ts +125 -0
- package/src/node/plugins/wx/styles/transform-wx-style.ts +11 -0
- package/src/node/plugins/wx/styles/utils.ts +119 -0
- package/src/node/utils/modules.ts +4 -3
- package/src/node/utils/packages.ts +2 -2
- package/src/node/utils/vite.ts +67 -0
- package/src/node/vite-plugin.ts +0 -2
- package/dist/node/plugins/css/plugins.d.ts +0 -4
- package/dist/node/plugins/css/plugins.js +0 -119
- package/dist/node/plugins/wx/dev/create-wx-dev-mode.d.ts +0 -6
- package/dist/node/plugins/wx/dev/create-wx-dev-mode.js +0 -9
- package/dist/node/utils/serialized-task-queue.d.ts +0 -13
- package/dist/node/utils/serialized-task-queue.js +0 -25
- package/src/node/plugins/css/plugins.ts +0 -135
- package/src/node/plugins/wx/dev/create-wx-dev-mode.ts +0 -14
- package/src/node/utils/serialized-task-queue.ts +0 -32
|
@@ -1,41 +1,92 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import colors from 'picocolors';
|
|
3
4
|
import { dev } from 'rolldown/experimental';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { asyncScheduler } from 'rxjs';
|
|
6
|
+
import { createHmrResultsStream } from './create-hmr-results-stream.js';
|
|
7
|
+
import { createStyleCapture } from './create-style-capture.js';
|
|
8
|
+
import { developmentAppWxssFileName, hmrControlPath, hmrInfoFileName, hmrPatchesFileName, renderDevelopmentAppWxss, renderHmrInfo, renderInitialHmrPatches, writeHmrFile } from './hmr-files.js';
|
|
9
|
+
import { createHostActions } from './host-actions.js';
|
|
6
10
|
import { PatchPublisher } from './patch-publisher.js';
|
|
11
|
+
import { createRuntimeReportsStream } from './runtime-reports.js';
|
|
7
12
|
import { installWxDevOptions, requireSingleOutput } from './wx-dev-options.js';
|
|
13
|
+
/** One short trailing-edge window absorbs editor bursts before style preparation and physical Page notification. */
|
|
14
|
+
const hmrSettleMilliseconds = 32;
|
|
8
15
|
/**
|
|
9
16
|
* Creates the wx dev host: the adapter that owns the physical Rolldown DevEngine (created
|
|
10
17
|
* with dev(...)) and the patch publisher, and replaces Vite's bundledDev.listen so the
|
|
11
18
|
* engine writes directly to the Mini Program output directory instead of serving browser
|
|
12
19
|
* HMR over HTTP.
|
|
20
|
+
*
|
|
21
|
+
* `applicationEntryIds` is the resolver's immutable cascade policy, not a second graph: it selects the App capsule followed
|
|
22
|
+
* by configured Page capsules from Rolldown's larger entry set. Rolldown remains the authority for every live import edge.
|
|
13
23
|
*/
|
|
14
|
-
export async function createWxDevHost({ server, options }) {
|
|
24
|
+
export async function createWxDevHost({ server, options, applicationEntryIds }) {
|
|
15
25
|
const bundledDev = getBundledDev(server);
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
const
|
|
26
|
+
// All callbacks admit typed actions through this edge; concatMap is the sole owner of effect ordering and mutable host state.
|
|
27
|
+
const hostActions = createHostActions(applyHostAction, (action, error) => logWxError(server.config.logger, `wx HMR ${action.kind} failed`, error));
|
|
28
|
+
const styleCapture = createStyleCapture({
|
|
29
|
+
applicationEntryIds: applicationEntryIds,
|
|
30
|
+
outDir: server.config.build.outDir,
|
|
31
|
+
// Capture hooks run only after engine.run, when the host action subscription and all reducer dependencies are ready.
|
|
32
|
+
emit: (action) => hostActions.next(action),
|
|
33
|
+
transformTailwindRoot: async (rootId, requestId) => {
|
|
34
|
+
// The capture contains generated CSS, while the sidecar must re-run from raw Tailwind directives. Vite exposes no
|
|
35
|
+
// raw source in its live module graph, so this read is the authoritative source generation rather than a read-back.
|
|
36
|
+
return server.environments.client.pluginContainer.transform(await readFile(rootId, 'utf8'), requestId);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
19
39
|
const publisher = new PatchPublisher((content) => writeHmrFile(server.config.build.outDir, hmrPatchesFileName, content));
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
const initialBuild = installWxDevOptions({ bundledDev, server, options });
|
|
40
|
+
// Option installation now configures only Rolldown. Build lifecycle results enter through the engine's output action below.
|
|
41
|
+
installWxDevOptions({ bundledDev, server, options, hostPlugins: [styleCapture.plugin] });
|
|
23
42
|
const engine = await createEngine();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
const hmrResults = createHmrResultsStream(hmrSettleMilliseconds, asyncScheduler, (result) => {
|
|
44
|
+
// One reduced window becomes one existing host transaction: style preparation, one cumulative patch write, then
|
|
45
|
+
// ordered delivery notifications for every original Rolldown payload retained by the stream.
|
|
46
|
+
hostActions.next({ kind: 'publish', result: result });
|
|
47
|
+
}, (error) => {
|
|
48
|
+
/*
|
|
49
|
+
* Invalid syntax is a normal intermediate editor generation. Rolldown produced no patch, so neither the published
|
|
50
|
+
* nor applied runtime frontier needs repair. Triggering a complete build here would immediately compile the same
|
|
51
|
+
* invalid source and empirically wedges that DevEngine generation. Admit only the diagnostic; when the user saves
|
|
52
|
+
* valid source, the watch engine emits an ordinary successful callback and resumes the normal publication path.
|
|
53
|
+
*/
|
|
54
|
+
hostActions.next({ kind: 'error', error: error });
|
|
55
|
+
});
|
|
56
|
+
const runtimeReports = createRuntimeReportsStream(hmrSettleMilliseconds, asyncScheduler, (reports) => {
|
|
57
|
+
// ACK conflation happens before admission, but publisher validation and mutation remain ordered with patch writes and
|
|
58
|
+
// build rotation. Reports arriving during publication therefore execute only after that physical transaction commits.
|
|
59
|
+
hostActions.next({ kind: 'reports', reports: reports });
|
|
60
|
+
});
|
|
61
|
+
/*
|
|
62
|
+
* BundledDev is Vite's mutable environment adapter. Transfer its engine slot to the one physical writer created above so
|
|
63
|
+
* Vite middleware and close() address the same engine; leaving the slot untouched would make listen() create a second,
|
|
64
|
+
* skip-write browser engine with a divergent graph. Disable Vite's access-triggered regeneration because runtime reports and
|
|
65
|
+
* Rolldown HMR results now own every rebuild decision. Finally replace listen so readiness waits for physical initial output
|
|
66
|
+
* instead of Vite's in-memory bundle. These assignments are installation-time ownership transfer, never per-update state.
|
|
67
|
+
*/
|
|
28
68
|
bundledDev._devEngine = engine;
|
|
29
69
|
bundledDev.triggerBundleRegenerationIfStale = async () => false;
|
|
30
70
|
bundledDev.listen = async () => {
|
|
31
|
-
|
|
71
|
+
/*
|
|
72
|
+
* Subscribe before run because DevEngine intentionally fulfills run() after an initial plugin failure and emits that
|
|
73
|
+
* Error through onOutput. Observing the existing action edge preserves the exact failure without a second buildEnd
|
|
74
|
+
* Promise or forwarding Subject; waitForAction does not consume the action from concatMap's reducer subscription.
|
|
75
|
+
*/
|
|
76
|
+
const initialOutput = hostActions.waitForAction((action) => action.kind === 'output');
|
|
77
|
+
await engine.run();
|
|
78
|
+
const { result } = await initialOutput;
|
|
79
|
+
if (result instanceof Error) {
|
|
80
|
+
throw result;
|
|
81
|
+
}
|
|
32
82
|
await engine.ensureCurrentBuildFinish();
|
|
33
83
|
};
|
|
34
84
|
// Vite binds the port only after initServer (and therefore the initial build) completes, so
|
|
35
85
|
// the actual port is not observable while onOutput runs for the first build. The App metadata
|
|
36
86
|
// is written once the port is real; later full builds rewrite it from onOutput.
|
|
37
87
|
server.httpServer?.once('listening', () => {
|
|
38
|
-
|
|
88
|
+
// Port availability is lifecycle data, so initial identity rotation follows prior output/capture actions in the reducer.
|
|
89
|
+
hostActions.next({ kind: 'listening' });
|
|
39
90
|
});
|
|
40
91
|
// The runtime's metadata-only reports land on the control path; the buildId in each report
|
|
41
92
|
// IS the Rolldown client ID.
|
|
@@ -43,8 +94,13 @@ export async function createWxDevHost({ server, options }) {
|
|
|
43
94
|
installDevToolsPrinter(server);
|
|
44
95
|
return {
|
|
45
96
|
close: async () => {
|
|
46
|
-
|
|
47
|
-
|
|
97
|
+
// Quiet-window completion first admits final HMR publications and rebuild reports into the serialized action edge.
|
|
98
|
+
hmrResults.complete();
|
|
99
|
+
runtimeReports.complete();
|
|
100
|
+
await hostActions.waitForIdle();
|
|
101
|
+
// Keep the action edge open until the final generation has admitted all capture and output callbacks.
|
|
102
|
+
await engine.ensureCurrentBuildFinish();
|
|
103
|
+
await hostActions.complete();
|
|
48
104
|
}
|
|
49
105
|
};
|
|
50
106
|
/** Rotates the build identity and materializes the App metadata for it. */
|
|
@@ -62,6 +118,10 @@ export async function createWxDevHost({ server, options }) {
|
|
|
62
118
|
await engine.registerClient(buildId);
|
|
63
119
|
await publishBuildMetadata(server, buildId, port);
|
|
64
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Mutates only the request-local HTTP response: unsupported methods become 404, malformed bounded bodies become 400, and a
|
|
123
|
+
* valid report is admitted before the default 200 response ends. No response object or parser state crosses requests.
|
|
124
|
+
*/
|
|
65
125
|
async function handleReport(req, res) {
|
|
66
126
|
if (req.method !== 'POST') {
|
|
67
127
|
res.statusCode = 404;
|
|
@@ -70,7 +130,7 @@ export async function createWxDevHost({ server, options }) {
|
|
|
70
130
|
}
|
|
71
131
|
try {
|
|
72
132
|
const report = JSON.parse(await readBody(req));
|
|
73
|
-
|
|
133
|
+
runtimeReports.next(report);
|
|
74
134
|
res.end();
|
|
75
135
|
}
|
|
76
136
|
catch (e) {
|
|
@@ -79,6 +139,36 @@ export async function createWxDevHost({ server, options }) {
|
|
|
79
139
|
res.end();
|
|
80
140
|
}
|
|
81
141
|
}
|
|
142
|
+
/** Reduces one merged source action through the existing authoritative host state. */
|
|
143
|
+
function applyHostAction(action) {
|
|
144
|
+
switch (action.kind) {
|
|
145
|
+
case 'capture-graph':
|
|
146
|
+
// The buildStart reader is a live capability; replacing it only here makes the reducer own build rebinding.
|
|
147
|
+
styleCapture.captureGraph(action.getModuleInfo);
|
|
148
|
+
return;
|
|
149
|
+
case 'capture-style':
|
|
150
|
+
// A failed upstream transform emits no action, intentionally retaining the last valid processed CSS generation.
|
|
151
|
+
styleCapture.captureStyle(action.id, action.style);
|
|
152
|
+
return;
|
|
153
|
+
case 'publish':
|
|
154
|
+
return publishUpdates(action.result);
|
|
155
|
+
case 'error':
|
|
156
|
+
logWxError(server.config.logger, 'wx HMR update failed', action.error);
|
|
157
|
+
return;
|
|
158
|
+
case 'reports':
|
|
159
|
+
action.reports.forEach(processReport);
|
|
160
|
+
return;
|
|
161
|
+
case 'output':
|
|
162
|
+
if (action.result instanceof Error) {
|
|
163
|
+
logWxError(server.config.logger, 'wx dev build failed', action.result);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
styleCapture.bindOutput(action.result.output);
|
|
167
|
+
return rotateBuildSession();
|
|
168
|
+
case 'listening':
|
|
169
|
+
return rotateBuildSession();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
82
172
|
/** Applies one runtime receipt to the active physical patch history. */
|
|
83
173
|
function processReport(report) {
|
|
84
174
|
// Delayed reports from older builds must never prune the live build's cumulative patch history.
|
|
@@ -87,8 +177,7 @@ export async function createWxDevHost({ server, options }) {
|
|
|
87
177
|
}
|
|
88
178
|
switch (report.kind) {
|
|
89
179
|
case 'rebuild': {
|
|
90
|
-
|
|
91
|
-
engine.triggerFullBuild();
|
|
180
|
+
requestFullBuild(report.reason);
|
|
92
181
|
return;
|
|
93
182
|
}
|
|
94
183
|
case 'applied': {
|
|
@@ -102,43 +191,67 @@ export async function createWxDevHost({ server, options }) {
|
|
|
102
191
|
const rolldownOptions = await bundledDev.getRolldownOptions();
|
|
103
192
|
const output = requireSingleOutput(rolldownOptions);
|
|
104
193
|
return dev(rolldownOptions, output, {
|
|
105
|
-
|
|
106
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Admits Rolldown's non-awaited callback without starting asynchronous host work on the binding callback stack.
|
|
196
|
+
* The Subject retains Error values as control events and successful values as lossless patch data until its quiet edge.
|
|
197
|
+
*/
|
|
198
|
+
onHmrUpdates: (result) => {
|
|
199
|
+
hmrResults.next(result);
|
|
200
|
+
},
|
|
201
|
+
// Initial and later complete builds share one admission path; startup merely observes the first OutputAction.
|
|
202
|
+
onOutput: (result) => {
|
|
203
|
+
hostActions.next({ kind: 'output', result: result });
|
|
204
|
+
},
|
|
107
205
|
rebuildStrategy: 'never',
|
|
108
|
-
watch: {
|
|
206
|
+
watch: {
|
|
207
|
+
// Rolldown must observe every source generation and emit every incremental factory: later patches do not
|
|
208
|
+
// reconstruct modules changed only by an earlier callback. RxJS conflates publication after compilation,
|
|
209
|
+
// preserving the complete patch sequence while avoiding repeated physical DevTools notifications.
|
|
210
|
+
skipWrite: false,
|
|
211
|
+
useDebounce: false
|
|
212
|
+
}
|
|
109
213
|
});
|
|
110
214
|
}
|
|
111
|
-
/**
|
|
112
|
-
function
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
hostTasks.enqueue('wx HMR publication failed', () => publishUpdates(result));
|
|
215
|
+
/** Centralizes the one diagnostic and DevEngine command used by every rebuild authority. */
|
|
216
|
+
function requestFullBuild(reason) {
|
|
217
|
+
server.config.logger.info(`[vpt] wx full rebuild required${reason ? `: ${reason}` : ''}`);
|
|
218
|
+
engine.triggerFullBuild();
|
|
118
219
|
}
|
|
119
|
-
/**
|
|
220
|
+
/** Selects active updates and publishes their resulting host transaction. */
|
|
120
221
|
async function publishUpdates(result) {
|
|
121
|
-
|
|
222
|
+
/*
|
|
223
|
+
* This transaction-local array preserves every patch in one pass and permits immediate rebuild dominance. It never escapes
|
|
224
|
+
* the serialized action. A filter/find/flatMap pipeline would scan a large burst repeatedly and allocate an intermediate.
|
|
225
|
+
*/
|
|
226
|
+
const patches = [];
|
|
122
227
|
for (const { clientId, update } of result.updates) {
|
|
123
228
|
if (!publisher.isCurrentBuild(clientId) || update.type === 'Noop') {
|
|
124
229
|
continue;
|
|
125
230
|
}
|
|
126
231
|
if (update.type === 'Patch') {
|
|
127
|
-
|
|
232
|
+
patches.push(update);
|
|
128
233
|
continue;
|
|
129
234
|
}
|
|
130
|
-
|
|
131
|
-
engine.triggerFullBuild();
|
|
235
|
+
requestFullBuild(update.reason);
|
|
132
236
|
return;
|
|
133
237
|
}
|
|
134
|
-
|
|
238
|
+
await publishPatchBatch(patches);
|
|
239
|
+
}
|
|
240
|
+
/** Publishes one coherent WXSS, patch-file, and Rolldown-frontier transaction. */
|
|
241
|
+
async function publishPatchBatch(patches) {
|
|
242
|
+
if (patches.length === 0) {
|
|
135
243
|
return;
|
|
136
244
|
}
|
|
245
|
+
// `onHmrUpdates` is the transaction boundary after every affected transform has updated graph and candidate state.
|
|
246
|
+
// Every non-CSS edit may alter imports or Tailwind classes; rendering broadly and comparing finalized bytes avoids
|
|
247
|
+
// source scanning while preventing unrelated JavaScript edits from notifying DevTools through an identical rename.
|
|
248
|
+
await styleCapture.publishChanged(patches.flatMap((patch) => patch.changedIds));
|
|
249
|
+
// Publish global.wxss before the matching JavaScript patch so DevTools observes a coherent HMR transaction.
|
|
137
250
|
// The physical file must exist before Rolldown advances: once committed, later patches may be generated relative to
|
|
138
251
|
// this batch even if DevTools has not observed its file event yet. PatchPublisher keeps the unapplied range cumulative,
|
|
139
252
|
// so any later file generation still carries every factory needed to bridge the runtime's older application frontier.
|
|
140
|
-
await publisher.produce(
|
|
141
|
-
await commitPublishedBatch(
|
|
253
|
+
await publisher.produce(patches);
|
|
254
|
+
await commitPublishedBatch(patches);
|
|
142
255
|
}
|
|
143
256
|
/**
|
|
144
257
|
* Advances Rolldown's published frontier in the same sequence order materialized in the cumulative physical file.
|
|
@@ -155,14 +268,6 @@ export async function createWxDevHost({ server, options }) {
|
|
|
155
268
|
await engine.notifyPayloadDelivered(patch.filename);
|
|
156
269
|
}
|
|
157
270
|
}
|
|
158
|
-
/** Rotates metadata after each successful complete output. */
|
|
159
|
-
function handleDevOutput(result) {
|
|
160
|
-
if (result instanceof Error) {
|
|
161
|
-
logWxError(server.config.logger, 'wx dev build failed', result);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
hostTasks.enqueue('wx dev build finalization failed', rotateBuildSession);
|
|
165
|
-
}
|
|
166
271
|
}
|
|
167
272
|
/** The bound HTTP port, or undefined before Vite's server is listening. */
|
|
168
273
|
function boundPort(server) {
|
|
@@ -176,7 +281,7 @@ function boundPort(server) {
|
|
|
176
281
|
}
|
|
177
282
|
return address.port;
|
|
178
283
|
}
|
|
179
|
-
/** Resets physical patches
|
|
284
|
+
/** Resets physical patches and then exposes one coherent build identity to a freshly compiled App heap. */
|
|
180
285
|
async function publishBuildMetadata(server, buildId, port) {
|
|
181
286
|
const info = {
|
|
182
287
|
buildId,
|
|
@@ -184,9 +289,21 @@ async function publishBuildMetadata(server, buildId, port) {
|
|
|
184
289
|
};
|
|
185
290
|
await writeHmrFile(server.config.build.outDir, hmrPatchesFileName, renderInitialHmrPatches());
|
|
186
291
|
await writeHmrFile(server.config.build.outDir, hmrInfoFileName, renderHmrInfo(info));
|
|
292
|
+
// `removeDevelopmentAppWxss` kept the previous physical wrapper in place while the complete output was written. Replace
|
|
293
|
+
// it only now, after the empty patch frontier and matching identity are durable. DevTools treats `app.wxss` as an App root,
|
|
294
|
+
// so this write intentionally causes the one full refresh allowed at a complete-build boundary; the refreshed App reads
|
|
295
|
+
// the new info above. Incremental updates must never write this file because an App refresh could destroy the heap while
|
|
296
|
+
// its JavaScript patch is being acknowledged. They publish only the imported `assets/global.wxss` stylesheet instead.
|
|
297
|
+
await writeHmrFile(server.config.build.outDir, developmentAppWxssFileName, renderDevelopmentAppWxss(buildId));
|
|
187
298
|
}
|
|
188
299
|
/** Replaces browser server URLs with the physical project directory consumed by WeChat DevTools. */
|
|
189
300
|
function installDevToolsPrinter(server) {
|
|
301
|
+
/*
|
|
302
|
+
* Vite exposes printing as a mutable server callback because URLs are unknown until startup. WX serves control reports over
|
|
303
|
+
* HTTP but users open the physical output directory, so retaining the browser printer advertises unusable navigation URLs.
|
|
304
|
+
* Replacing only this presentation callback leaves resolved URLs and server routing untouched and naturally dies with the
|
|
305
|
+
* server instance; logging elsewhere would duplicate Vite's one readiness notification.
|
|
306
|
+
*/
|
|
190
307
|
server.printUrls = () => {
|
|
191
308
|
server.config.logger.info(` ${colors.green('➜')} ${colors.bold('WeChat DevTools')}: ${colors.cyan(relativeToViteConfig(server.config.build.outDir, server.config.configFile, server.config.root))}`);
|
|
192
309
|
};
|
|
@@ -214,6 +331,12 @@ function logWxError(logger, prefix, error) {
|
|
|
214
331
|
const maximumBodyBytes = 64 * 1024;
|
|
215
332
|
function readBody(req) {
|
|
216
333
|
return new Promise((resolve, reject) => {
|
|
334
|
+
/*
|
|
335
|
+
* Node delivers one HTTP body through multiple callback invocations, so this request-local mutable string is the minimal
|
|
336
|
+
* state that joins chunks in wire order. The 64 KiB bound caps both retained memory and repeated-concatenation work; the
|
|
337
|
+
* value is parsed once on end and then discarded. Parsing per chunk is invalid because JSON tokens may cross boundaries,
|
|
338
|
+
* while retaining request bodies in host state would couple unrelated Page reports and leak across the server lifecycle.
|
|
339
|
+
*/
|
|
217
340
|
let body = '';
|
|
218
341
|
req.on('data', (chunk) => {
|
|
219
342
|
body += chunk.toString('utf8');
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { BindingClientHmrUpdate } from 'rolldown/experimental';
|
|
2
|
+
export declare const developmentAppWxssFileName = "app.wxss";
|
|
3
|
+
export declare const globalWxssFileName = "assets/global.wxss";
|
|
2
4
|
export declare const hmrInfoFileName = "hmr/info.js";
|
|
3
5
|
export declare const hmrPatchesFileName = "hmr/patches.js";
|
|
4
6
|
/** Report endpoint path served by the wx dev control edge. */
|
|
@@ -16,6 +18,14 @@ export type PatchUpdate = Extract<BindingClientHmrUpdate['update'], {
|
|
|
16
18
|
export declare function renderHmrInfo(info: HmrInfo): string;
|
|
17
19
|
/** Provides a valid dependency before the host has a patch range to publish. */
|
|
18
20
|
export declare function renderInitialHmrPatches(): string;
|
|
21
|
+
/**
|
|
22
|
+
* Renders the native App style entry for one complete development build.
|
|
23
|
+
*
|
|
24
|
+
* Imported global CSS can change while the entry remains byte-identical. DevTools may then reuse an older persistent compile
|
|
25
|
+
* cache after it restarts. Tying this inert comment to the build identity invalidates that cache once per complete build,
|
|
26
|
+
* while incremental style updates continue changing only the imported file and therefore preserve the App heap.
|
|
27
|
+
*/
|
|
28
|
+
export declare function renderDevelopmentAppWxss(buildId: string): string;
|
|
19
29
|
/**
|
|
20
30
|
* Renders the cumulative patch suffix as inert CommonJS data.
|
|
21
31
|
*
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
export const developmentAppWxssFileName = 'app.wxss';
|
|
5
|
+
export const globalWxssFileName = 'assets/global.wxss';
|
|
4
6
|
export const hmrInfoFileName = 'hmr/info.js';
|
|
5
7
|
export const hmrPatchesFileName = 'hmr/patches.js';
|
|
6
8
|
/** Report endpoint path served by the wx dev control edge. */
|
|
@@ -13,6 +15,16 @@ export function renderHmrInfo(info) {
|
|
|
13
15
|
export function renderInitialHmrPatches() {
|
|
14
16
|
return 'module.exports = undefined;\n';
|
|
15
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Renders the native App style entry for one complete development build.
|
|
20
|
+
*
|
|
21
|
+
* Imported global CSS can change while the entry remains byte-identical. DevTools may then reuse an older persistent compile
|
|
22
|
+
* cache after it restarts. Tying this inert comment to the build identity invalidates that cache once per complete build,
|
|
23
|
+
* while incremental style updates continue changing only the imported file and therefore preserve the App heap.
|
|
24
|
+
*/
|
|
25
|
+
export function renderDevelopmentAppWxss(buildId) {
|
|
26
|
+
return `@import "./assets/global.wxss";\n/* vpt-build:${buildId} */\n`;
|
|
27
|
+
}
|
|
16
28
|
/**
|
|
17
29
|
* Renders the cumulative patch suffix as inert CommonJS data.
|
|
18
30
|
*
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type HostActions<Action> = Readonly<{
|
|
2
|
+
complete: () => Promise<void>;
|
|
3
|
+
next: (action: Action) => void;
|
|
4
|
+
waitForAction: <Selected extends Action>(select: (action: Action) => action is Selected) => Promise<Selected>;
|
|
5
|
+
waitForIdle: () => Promise<void>;
|
|
6
|
+
}>;
|
|
7
|
+
/**
|
|
8
|
+
* Executes semantic host actions one at a time while keeping source admission synchronous.
|
|
9
|
+
*
|
|
10
|
+
* `concatMap` is the sole effect serializer: each deferred reducer call starts only after the prior Promise settles. Errors are
|
|
11
|
+
* converted to empty inner streams so one failed physical transaction is reported without terminating later host actions.
|
|
12
|
+
* Barrier envelopes participate in the same ordering but run no effect; resolving one proves every action admitted before it
|
|
13
|
+
* has settled. `waitForAction` observes this same admission edge for lifecycle gates without introducing a forwarding Subject.
|
|
14
|
+
* Completing the source drains already-admitted effects and gives shutdown one Promise for final quiescence.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createHostActions<Action>(apply: (action: Action) => void | Promise<void>, reportError: (action: Action, error: unknown) => void): HostActions<Action>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { catchError, concatMap, defer, EMPTY, filter, firstValueFrom, lastValueFrom, map, Subject } from 'rxjs';
|
|
2
|
+
/**
|
|
3
|
+
* Executes semantic host actions one at a time while keeping source admission synchronous.
|
|
4
|
+
*
|
|
5
|
+
* `concatMap` is the sole effect serializer: each deferred reducer call starts only after the prior Promise settles. Errors are
|
|
6
|
+
* converted to empty inner streams so one failed physical transaction is reported without terminating later host actions.
|
|
7
|
+
* Barrier envelopes participate in the same ordering but run no effect; resolving one proves every action admitted before it
|
|
8
|
+
* has settled. `waitForAction` observes this same admission edge for lifecycle gates without introducing a forwarding Subject.
|
|
9
|
+
* Completing the source drains already-admitted effects and gives shutdown one Promise for final quiescence.
|
|
10
|
+
*/
|
|
11
|
+
export function createHostActions(apply, reportError) {
|
|
12
|
+
/*
|
|
13
|
+
* This Subject is the host's only mutable admission edge. Independent callback stacks synchronously append semantic actions
|
|
14
|
+
* or barriers; concatMap owns the queued ordering and admits exactly one asynchronous effect at a time. A Promise chain would
|
|
15
|
+
* require mutation at every producer and is permanently rejected after one failure, while mergeMap could overlap physical
|
|
16
|
+
* writes and build rotation. The Subject retains no business frontier itself and is completed only after all external sources
|
|
17
|
+
* are quiescent, allowing lastValueFrom to prove the final queued effect settled.
|
|
18
|
+
*/
|
|
19
|
+
const envelopes = new Subject();
|
|
20
|
+
const completion = lastValueFrom(envelopes.pipe(concatMap((envelope) => {
|
|
21
|
+
if (envelope.kind === 'barrier') {
|
|
22
|
+
return defer(() => {
|
|
23
|
+
envelope.resolve();
|
|
24
|
+
return EMPTY;
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return defer(() => Promise.resolve(apply(envelope.action))).pipe(catchError((error) => {
|
|
28
|
+
reportError(envelope.action, error);
|
|
29
|
+
return EMPTY;
|
|
30
|
+
}));
|
|
31
|
+
})), { defaultValue: undefined });
|
|
32
|
+
return {
|
|
33
|
+
complete: async () => {
|
|
34
|
+
envelopes.complete();
|
|
35
|
+
await completion;
|
|
36
|
+
},
|
|
37
|
+
next(action) {
|
|
38
|
+
envelopes.next({ kind: 'action', action: action });
|
|
39
|
+
},
|
|
40
|
+
waitForAction(select) {
|
|
41
|
+
/*
|
|
42
|
+
* Subscribe to the multicast admission Subject rather than creating another lifecycle source. Barrier envelopes are
|
|
43
|
+
* internal ordering controls, so expose only semantic actions and let the caller's type guard select its milestone.
|
|
44
|
+
* firstValueFrom unsubscribes after the match; concatMap's independent subscription still applies the same action.
|
|
45
|
+
*/
|
|
46
|
+
return firstValueFrom(envelopes.pipe(filter((envelope) => envelope.kind === 'action'), map((envelope) => envelope.action), filter(select)));
|
|
47
|
+
},
|
|
48
|
+
waitForIdle() {
|
|
49
|
+
/*
|
|
50
|
+
* The resolver is one transaction-local mutable latch. Enqueuing it behind ordinary actions makes resolution mean
|
|
51
|
+
* every earlier effect settled, without completing the shared source needed by final DevEngine output callbacks.
|
|
52
|
+
*/
|
|
53
|
+
const barrier = Promise.withResolvers();
|
|
54
|
+
envelopes.next({ kind: 'barrier', resolve: barrier.resolve });
|
|
55
|
+
return barrier.promise;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type PatchUpdate } from './hmr-files.ts';
|
|
2
2
|
/** Abstracts the physical patches write; the engine owns the file destination. */
|
|
3
|
-
|
|
3
|
+
type WritePatches = (content: string) => Promise<void>;
|
|
4
4
|
/**
|
|
5
5
|
* Owns the cumulative sequence range between the host's published frontier and the runtime's applied frontier.
|
|
6
6
|
*
|
|
@@ -17,7 +17,6 @@ export type WritePatches = (content: string) => Promise<void>;
|
|
|
17
17
|
export declare class PatchPublisher {
|
|
18
18
|
private readonly writePatches;
|
|
19
19
|
private buildId;
|
|
20
|
-
/** Executable updates retained until the runtime confirms successful application. */
|
|
21
20
|
private readonly pendingPatches;
|
|
22
21
|
constructor(writePatches: WritePatches);
|
|
23
22
|
/** True when the buildId belongs to the current full build. */
|
|
@@ -40,3 +39,4 @@ export declare class PatchPublisher {
|
|
|
40
39
|
*/
|
|
41
40
|
acknowledge(seq: number): void;
|
|
42
41
|
}
|
|
42
|
+
export {};
|
|
@@ -15,8 +15,20 @@ import { renderHmrPatches } from './hmr-files.js';
|
|
|
15
15
|
*/
|
|
16
16
|
export class PatchPublisher {
|
|
17
17
|
writePatches;
|
|
18
|
+
/*
|
|
19
|
+
* This mutable identity is the sole session authority for runtime reports and per-client Rolldown updates. startBuild rotates
|
|
20
|
+
* it only after a successful complete output reaches the serialized host reducer. Delayed reports retain their original ID
|
|
21
|
+
* and therefore fail isCurrentBuild instead of pruning or rebuilding the new session. An immutable constructor value cannot
|
|
22
|
+
* represent full-build rotation, while deriving identity from patches would leave empty builds without a client ID.
|
|
23
|
+
*/
|
|
18
24
|
buildId;
|
|
19
|
-
|
|
25
|
+
/*
|
|
26
|
+
* This mutable ordered suffix bridges two independent frontiers: Rolldown payloads become published immediately after the
|
|
27
|
+
* cumulative file is durable, but entries remain here until the runtime acknowledges applying their sequence. Appending is
|
|
28
|
+
* lossless because later patches are deltas; prefix removal is the only valid mutation because acknowledgements are monotonic
|
|
29
|
+
* frontiers. A Set, latest-only value, or immutable reconstruction would either lose duplicate filenames/order or copy the
|
|
30
|
+
* complete unapplied history for every burst.
|
|
31
|
+
*/
|
|
20
32
|
pendingPatches = [];
|
|
21
33
|
// Explicit field assignment: node --test strips types and does not support parameter properties.
|
|
22
34
|
constructor(writePatches) {
|
|
@@ -52,6 +64,10 @@ export class PatchPublisher {
|
|
|
52
64
|
* retaining the gap between published and applied frontiers is what lets DevTools safely miss intermediate file events.
|
|
53
65
|
*/
|
|
54
66
|
acknowledge(seq) {
|
|
67
|
+
/*
|
|
68
|
+
* This local counter scans the monotonic prefix without allocating or invoking a callback for each patch. It exists only
|
|
69
|
+
* long enough to supply splice's prefix length; pendingPatches remains the sole durable frontier and is mutated once.
|
|
70
|
+
*/
|
|
55
71
|
let appliedCount = 0;
|
|
56
72
|
while (appliedCount < this.pendingPatches.length && this.pendingPatches[appliedCount].seq <= seq) {
|
|
57
73
|
appliedCount++;
|
|
@@ -1,10 +1,33 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type PluginOption } from 'vite';
|
|
2
2
|
import type { VitePluginTaroOptions } from '../../../../options.ts';
|
|
3
|
+
/** Selects the sole Vite environment that owns the physical Mini Program development project. */
|
|
4
|
+
export declare function isWxClientEnvironment(environment: Readonly<{
|
|
5
|
+
name: string;
|
|
6
|
+
}>): boolean;
|
|
3
7
|
/**
|
|
4
8
|
* Adds the serve-only bundled-development plugin set for the wx target: the dev adapter,
|
|
5
9
|
* Page HMR activation, and React Refresh adaptation transforms.
|
|
10
|
+
*
|
|
11
|
+
* The ordered application entries cross this configuration boundary unchanged so the host can compose global CSS without
|
|
12
|
+
* reconstructing the resolver's App/Page ownership policy from unrelated Rolldown shell and transport entries.
|
|
6
13
|
*/
|
|
7
|
-
export declare function createWxDevelopmentPlugin(options: VitePluginTaroOptions): PluginOption[];
|
|
14
|
+
export declare function createWxDevelopmentPlugin(options: VitePluginTaroOptions, applicationEntryIds: readonly string[]): PluginOption[];
|
|
15
|
+
/**
|
|
16
|
+
* Transfers development ownership of `app.wxss` from complete output to the dev host.
|
|
17
|
+
*
|
|
18
|
+
* The ordinary WX output hook emits the static production wrapper during every complete build. In development, the existing
|
|
19
|
+
* physical wrapper instead contains the previous build ID. If the static asset remained in this bundle, the DevEngine would:
|
|
20
|
+
*
|
|
21
|
+
* 1. replace the versioned wrapper with static production bytes while JavaScript and HMR metadata are still being finalized;
|
|
22
|
+
* 2. let DevTools observe that root-style change and reload the App against the previous build identity;
|
|
23
|
+
* 3. let the host replace the wrapper again with the new build ID, causing a second App reload.
|
|
24
|
+
*
|
|
25
|
+
* Deleting only the in-memory bundle entry avoids both premature writes. Development sets `emptyOutDir: false`, so the prior
|
|
26
|
+
* physical `app.wxss` remains available while the complete output is written. The host replaces it exactly once after
|
|
27
|
+
* `hmr/patches.js` and `hmr/info.js` are durable. Incremental HMR never enters this complete-output hook and continues changing
|
|
28
|
+
* only `assets/global.wxss`, which preserves the App heap. The serve-only plugin leaves production output unchanged.
|
|
29
|
+
*/
|
|
30
|
+
export declare function removeDevelopmentAppWxss(bundle: Record<string, unknown>): void;
|
|
8
31
|
/** Ensures the Refresh hook exists before React's renderer evaluates and injects itself. */
|
|
9
32
|
export declare function injectReactRefreshBootstrap(code: string): {
|
|
10
33
|
code: string;
|