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
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import colors from 'picocolors';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { once } from '../../../utils/once.js';
|
|
6
|
-
import { resolvePackageFile } from '../../../utils/packages.js';
|
|
7
|
-
import { appShellFileName } from '../module.js';
|
|
8
|
-
import { createWxDevMode } from './create-wx-dev-mode.js';
|
|
9
|
-
import { emptyOutputDirectory } from './empty-output-directory.js';
|
|
3
|
+
import { dev } from 'rolldown/experimental';
|
|
4
|
+
import { SerializedTaskQueue } from '../../../utils/serialized-task-queue.js';
|
|
10
5
|
import { hmrControlPath, hmrInfoFileName, hmrPatchesFileName, renderHmrInfo, renderInitialHmrPatches, writeHmrFile } from './hmr-files.js';
|
|
11
6
|
import { PatchPublisher } from './patch-publisher.js';
|
|
7
|
+
import { installWxDevOptions, requireSingleOutput } from './wx-dev-options.js';
|
|
12
8
|
/**
|
|
13
9
|
* Creates the wx dev host: the adapter that owns the physical Rolldown DevEngine (created
|
|
14
10
|
* with dev(...)) and the patch publisher, and replaces Vite's bundledDev.listen so the
|
|
@@ -17,20 +13,13 @@ import { PatchPublisher } from './patch-publisher.js';
|
|
|
17
13
|
*/
|
|
18
14
|
export async function createWxDevHost({ server, options }) {
|
|
19
15
|
const bundledDev = getBundledDev(server);
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
const
|
|
23
|
-
const settleInitialBuild = once((error) => {
|
|
24
|
-
if (error) {
|
|
25
|
-
initialBuild.reject(error);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
initialBuild.resolve();
|
|
29
|
-
}
|
|
30
|
-
});
|
|
16
|
+
// Rolldown invokes output callbacks without awaiting their promises. This queue is the single owner of mutable HMR host
|
|
17
|
+
// state and physical metadata writes, preventing a later patch or build identity from being overwritten by older work.
|
|
18
|
+
const hostTasks = new SerializedTaskQueue((operation, error) => logWxError(server.config.logger, operation, error));
|
|
31
19
|
const publisher = new PatchPublisher((content) => writeHmrFile(server.config.build.outDir, hmrPatchesFileName, content));
|
|
32
|
-
//
|
|
33
|
-
|
|
20
|
+
// DevEngine does not reject run() after an initial plugin failure. The options layer owns a first-build buildEnd barrier
|
|
21
|
+
// and exposes only its result; later build errors continue independently through onOutput and onHmrUpdates.
|
|
22
|
+
const initialBuild = installWxDevOptions({ bundledDev, server, options });
|
|
34
23
|
const engine = await createEngine();
|
|
35
24
|
// The wx dev host owns the only DevEngine. Vite's default listen() would create a second
|
|
36
25
|
// skip-write engine that renders into memory for browser HMR; instead the physical
|
|
@@ -39,30 +28,27 @@ export async function createWxDevHost({ server, options }) {
|
|
|
39
28
|
bundledDev._devEngine = engine;
|
|
40
29
|
bundledDev.triggerBundleRegenerationIfStale = async () => false;
|
|
41
30
|
bundledDev.listen = async () => {
|
|
42
|
-
await Promise.all([engine.run(), initialBuild
|
|
31
|
+
await Promise.all([engine.run(), initialBuild]);
|
|
43
32
|
await engine.ensureCurrentBuildFinish();
|
|
44
33
|
};
|
|
45
34
|
// Vite binds the port only after initServer (and therefore the initial build) completes, so
|
|
46
35
|
// the actual port is not observable while onOutput runs for the first build. The App metadata
|
|
47
36
|
// is written once the port is real; later full builds rewrite it from onOutput.
|
|
48
|
-
server.httpServer?.once('listening',
|
|
37
|
+
server.httpServer?.once('listening', () => {
|
|
38
|
+
hostTasks.enqueue('wx HMR initialization failed', rotateBuildSession);
|
|
39
|
+
});
|
|
49
40
|
// The runtime's metadata-only reports land on the control path; the buildId in each report
|
|
50
41
|
// IS the Rolldown client ID.
|
|
51
42
|
server.middlewares.use(hmrControlPath, (req, res) => void handleReport(req, res));
|
|
52
|
-
|
|
53
|
-
// opens the output directory directly, so the printed path is the one to select.
|
|
54
|
-
const originalPrintUrls = server.printUrls.bind(server);
|
|
55
|
-
server.printUrls = () => {
|
|
56
|
-
originalPrintUrls();
|
|
57
|
-
server.config.logger.info(` ${colors.green('➜')} ${colors.bold('WeChat DevTools')}: ${colors.cyan(relativeToViteConfig(server.config.build.outDir, server.config.configFile, server.config.root))}`);
|
|
58
|
-
};
|
|
43
|
+
installDevToolsPrinter(server);
|
|
59
44
|
return {
|
|
60
45
|
close: async () => {
|
|
46
|
+
await hostTasks.waitForIdle();
|
|
61
47
|
await engine.close();
|
|
62
48
|
}
|
|
63
49
|
};
|
|
64
50
|
/** Rotates the build identity and materializes the App metadata for it. */
|
|
65
|
-
async function
|
|
51
|
+
async function rotateBuildSession() {
|
|
66
52
|
const port = boundPort(server);
|
|
67
53
|
if (port === undefined) {
|
|
68
54
|
// The initial output finishes before Vite binds its port. The listening listener
|
|
@@ -74,10 +60,7 @@ export async function createWxDevHost({ server, options }) {
|
|
|
74
60
|
await engine.removeClient(previousBuildId);
|
|
75
61
|
}
|
|
76
62
|
await engine.registerClient(buildId);
|
|
77
|
-
|
|
78
|
-
// matching reset patch file.
|
|
79
|
-
await writeHmrFile(server.config.build.outDir, hmrPatchesFileName, renderInitialHmrPatches());
|
|
80
|
-
await writeHmrInfo(server, buildId, port);
|
|
63
|
+
await publishBuildMetadata(server, buildId, port);
|
|
81
64
|
}
|
|
82
65
|
async function handleReport(req, res) {
|
|
83
66
|
if (req.method !== 'POST') {
|
|
@@ -87,21 +70,7 @@ export async function createWxDevHost({ server, options }) {
|
|
|
87
70
|
}
|
|
88
71
|
try {
|
|
89
72
|
const report = JSON.parse(await readBody(req));
|
|
90
|
-
|
|
91
|
-
// delayed reports from older builds are ignored so they can never influence the
|
|
92
|
-
// live build.
|
|
93
|
-
if (!publisher.isCurrentBuild(report.buildId)) {
|
|
94
|
-
res.end();
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
if (report.kind === 'rebuild') {
|
|
98
|
-
engine.triggerFullBuild();
|
|
99
|
-
res.end();
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
// Commit every newly acknowledged Rolldown payload to this client's ship map.
|
|
103
|
-
const deliveredFiles = publisher.acknowledge(report.seq);
|
|
104
|
-
await Promise.all(deliveredFiles.map((fileName) => engine.notifyPayloadDelivered(fileName)));
|
|
73
|
+
await hostTasks.run(() => processReport(report));
|
|
105
74
|
res.end();
|
|
106
75
|
}
|
|
107
76
|
catch (e) {
|
|
@@ -110,105 +79,89 @@ export async function createWxDevHost({ server, options }) {
|
|
|
110
79
|
res.end();
|
|
111
80
|
}
|
|
112
81
|
}
|
|
82
|
+
/** Applies one runtime receipt to the active physical patch history. */
|
|
83
|
+
function processReport(report) {
|
|
84
|
+
// Delayed reports from older builds must never prune the live build's cumulative patch history.
|
|
85
|
+
if (!publisher.isCurrentBuild(report.buildId)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
switch (report.kind) {
|
|
89
|
+
case 'rebuild': {
|
|
90
|
+
server.config.logger.info(`[vpt] wx runtime requested a full rebuild: ${report.reason}`);
|
|
91
|
+
engine.triggerFullBuild();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
case 'applied': {
|
|
95
|
+
publisher.acknowledge(report.seq);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
113
100
|
/** Creates the physical DevEngine with the wx dev host hooks. */
|
|
114
101
|
async function createEngine() {
|
|
115
102
|
const rolldownOptions = await bundledDev.getRolldownOptions();
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
onHmrUpdates: async (result) => {
|
|
121
|
-
if (result instanceof Error) {
|
|
122
|
-
logWxError(server.config.logger, 'wx HMR update failed', result);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
// Collect the current client's batch first: one HMR event can carry several
|
|
126
|
-
// client envelopes, while only the active build may enter its patch history.
|
|
127
|
-
const batch = [];
|
|
128
|
-
for (const { clientId, update } of result.updates) {
|
|
129
|
-
// Removed and delayed client sessions can still appear in a completed
|
|
130
|
-
// engine batch; they must never enter the current build's patch history.
|
|
131
|
-
if (!publisher.isCurrentBuild(clientId) || update.type === 'Noop') {
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (update.type === 'Patch') {
|
|
135
|
-
batch.push(update);
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
server.config.logger.info(`[vpt] wx full rebuild required${update.reason ? `: ${update.reason}` : ''}`);
|
|
139
|
-
engine.triggerFullBuild();
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
if (batch.length > 0) {
|
|
143
|
-
await publisher.produce(batch);
|
|
144
|
-
// server.config.logger.info('[vpt] wx patch produced')
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
onOutput: async (result) => {
|
|
148
|
-
if (result instanceof Error) {
|
|
149
|
-
logWxError(server.config.logger, 'wx dev build failed', result);
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
// A fresh build identity per complete physical build; the App runtime reads it
|
|
153
|
-
// from hmr/info.js before any module registers.
|
|
154
|
-
await startFreshBuild();
|
|
155
|
-
},
|
|
103
|
+
const output = requireSingleOutput(rolldownOptions);
|
|
104
|
+
return dev(rolldownOptions, output, {
|
|
105
|
+
onHmrUpdates: handleHmrUpdates,
|
|
106
|
+
onOutput: handleDevOutput,
|
|
156
107
|
rebuildStrategy: 'never',
|
|
157
108
|
watch: { skipWrite: false }
|
|
158
109
|
});
|
|
159
110
|
}
|
|
160
|
-
/**
|
|
161
|
-
function
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
111
|
+
/** Converts Rolldown's non-awaited callback into one ordered host publication task. */
|
|
112
|
+
function handleHmrUpdates(result) {
|
|
113
|
+
if (result instanceof Error) {
|
|
114
|
+
logWxError(server.config.logger, 'wx HMR update failed', result);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
hostTasks.enqueue('wx HMR publication failed', () => publishUpdates(result));
|
|
118
|
+
}
|
|
119
|
+
/** Publishes only the active client's patches or requests the complete build required by Rolldown. */
|
|
120
|
+
async function publishUpdates(result) {
|
|
121
|
+
const batch = [];
|
|
122
|
+
for (const { clientId, update } of result.updates) {
|
|
123
|
+
if (!publisher.isCurrentBuild(clientId) || update.type === 'Noop') {
|
|
124
|
+
continue;
|
|
167
125
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (Array.isArray(configuredOutput)) {
|
|
172
|
-
throw new Error('wx development supports one configured Rolldown output.');
|
|
126
|
+
if (update.type === 'Patch') {
|
|
127
|
+
batch.push(update);
|
|
128
|
+
continue;
|
|
173
129
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
disableViteOxcSourcemap(rolldownOptions.plugins);
|
|
210
|
-
return rolldownOptions;
|
|
211
|
-
};
|
|
130
|
+
server.config.logger.info(`[vpt] wx full rebuild required${update.reason ? `: ${update.reason}` : ''}`);
|
|
131
|
+
engine.triggerFullBuild();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (batch.length === 0) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
// The physical file must exist before Rolldown advances: once committed, later patches may be generated relative to
|
|
138
|
+
// this batch even if DevTools has not observed its file event yet. PatchPublisher keeps the unapplied range cumulative,
|
|
139
|
+
// so any later file generation still carries every factory needed to bridge the runtime's older application frontier.
|
|
140
|
+
await publisher.produce(batch);
|
|
141
|
+
await commitPublishedBatch(batch);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Advances Rolldown's published frontier in the same sequence order materialized in the cumulative physical file.
|
|
145
|
+
*
|
|
146
|
+
* Given a batch [5, 6], publisher.produce has already made factories [5, 6] visible in hmr/patches.js. This method then
|
|
147
|
+
* commits payload 5 followed by payload 6. If DevTools observes neither event before sequence 7 is published, the next
|
|
148
|
+
* physical file contains [5, 6, 7], while Rolldown is free to generate 7 relative to its already-published sequence 6.
|
|
149
|
+
*/
|
|
150
|
+
async function commitPublishedBatch(batch) {
|
|
151
|
+
// Do not use Promise.all or deduplicate filenames. Multiple sequential payloads may target the same output filename,
|
|
152
|
+
// and each notification commits one distinct Rolldown payload. Awaiting in order preserves the exact frontier encoded
|
|
153
|
+
// by PatchUpdate.seq and prevents a later payload from becoming visible to the engine before its predecessor.
|
|
154
|
+
for (const patch of batch) {
|
|
155
|
+
await engine.notifyPayloadDelivered(patch.filename);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
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);
|
|
212
165
|
}
|
|
213
166
|
}
|
|
214
167
|
/** The bound HTTP port, or undefined before Vite's server is listening. */
|
|
@@ -223,14 +176,23 @@ function boundPort(server) {
|
|
|
223
176
|
}
|
|
224
177
|
return address.port;
|
|
225
178
|
}
|
|
226
|
-
/**
|
|
227
|
-
async function
|
|
179
|
+
/** Resets physical patches before exposing the matching immutable build identity to a new App heap. */
|
|
180
|
+
async function publishBuildMetadata(server, buildId, port) {
|
|
228
181
|
const info = {
|
|
229
182
|
buildId,
|
|
230
183
|
endpoint: `${server.config.server.https ? 'https' : 'http'}://${resolveEndpointHost(server)}:${port}${hmrControlPath}`
|
|
231
184
|
};
|
|
185
|
+
await writeHmrFile(server.config.build.outDir, hmrPatchesFileName, renderInitialHmrPatches());
|
|
232
186
|
await writeHmrFile(server.config.build.outDir, hmrInfoFileName, renderHmrInfo(info));
|
|
233
187
|
}
|
|
188
|
+
/** Appends the physical project directory after Vite's normal server URLs. */
|
|
189
|
+
function installDevToolsPrinter(server) {
|
|
190
|
+
const originalPrintUrls = server.printUrls.bind(server);
|
|
191
|
+
server.printUrls = () => {
|
|
192
|
+
originalPrintUrls();
|
|
193
|
+
server.config.logger.info(` ${colors.green('➜')} ${colors.bold('WeChat DevTools')}: ${colors.cyan(relativeToViteConfig(server.config.build.outDir, server.config.configFile, server.config.root))}`);
|
|
194
|
+
};
|
|
195
|
+
}
|
|
234
196
|
/**
|
|
235
197
|
* The project directory shown in the DevTools banner, relative to the Vite config:
|
|
236
198
|
* `dist/wx` instead of the absolute output path, so it can be pasted into DevTools.
|
|
@@ -251,20 +213,6 @@ function logWxError(logger, prefix, error) {
|
|
|
251
213
|
logger.error(`[vpt] ${prefix} with unknown error: ${error}`);
|
|
252
214
|
}
|
|
253
215
|
}
|
|
254
|
-
// The runtime source is immutable for the host's lifetime (it changes only when the
|
|
255
|
-
// plugin is rebuilt), so the nested bundle runs once and every build reuses it.
|
|
256
|
-
const bundleRuntimeSource = once(
|
|
257
|
-
/** Bundles the runtime host and state machine into one plain script for injection. */
|
|
258
|
-
async function bundleRuntimeSource() {
|
|
259
|
-
// write: false — only the code is consumed; without it rolldown drops the bundle into
|
|
260
|
-
// the default dist/ of the running project.
|
|
261
|
-
const result = await build({
|
|
262
|
-
input: resolvePackageFile('dist/runtime/wx/dev/dev-runtime.js'),
|
|
263
|
-
output: { format: 'iife', minify: true, sourcemap: false },
|
|
264
|
-
write: false
|
|
265
|
-
});
|
|
266
|
-
return result.output[0].code;
|
|
267
|
-
});
|
|
268
216
|
const maximumBodyBytes = 64 * 1024;
|
|
269
217
|
function readBody(req) {
|
|
270
218
|
return new Promise((resolve, reject) => {
|
|
@@ -292,67 +240,6 @@ function resolveEndpointHost(server) {
|
|
|
292
240
|
}
|
|
293
241
|
return address.address.includes(':') ? `[${address.address}]` : address.address;
|
|
294
242
|
}
|
|
295
|
-
/**
|
|
296
|
-
* Prepends entry banners. Banners are plain text appended after Rolldown's analysis, so the
|
|
297
|
-
* requires never become chunk dependencies (a bare require inside the injected runtime source
|
|
298
|
-
* would stall the build), and the wx render pipeline keeps this text after the hoisted chunk
|
|
299
|
-
* requires — so the runtime chunk exists before these run:
|
|
300
|
-
* - the app entry loads hmr/info.js and initializes the runtime before any module registers;
|
|
301
|
-
* - every page requires hmr/patches.js, the changed dependency that makes DevTools re-execute
|
|
302
|
-
* live Pages and thereby load physical updates.
|
|
303
|
-
*/
|
|
304
|
-
function createEntryBanner(pageFiles) {
|
|
305
|
-
return (chunk) => {
|
|
306
|
-
if (chunk.name === appShellFileName) {
|
|
307
|
-
return "__rolldown_runtime__.initialize(require('./hmr/info.js'));\n";
|
|
308
|
-
}
|
|
309
|
-
if (pageFiles.has(chunk.name)) {
|
|
310
|
-
// Page files live at `pages/<route>/index.js`, so the dependency path must be
|
|
311
|
-
// computed relative to each page's own directory.
|
|
312
|
-
const patchesPath = path.posix.relative(path.posix.dirname(chunk.fileName), 'hmr/patches.js');
|
|
313
|
-
return `require('${patchesPath}');\n`;
|
|
314
|
-
}
|
|
315
|
-
return '';
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
function createStableFileNames(addon, fallback) {
|
|
319
|
-
if (typeof addon === 'function') {
|
|
320
|
-
return (value) => toStableFileName(String(addon(value)));
|
|
321
|
-
}
|
|
322
|
-
return toStableFileName(typeof addon === 'string' ? addon : fallback);
|
|
323
|
-
}
|
|
324
|
-
function toStableFileName(fileName) {
|
|
325
|
-
return fileName
|
|
326
|
-
.replace(/(^|\/)\[hash(?::\d+)?\](?=\.|$)/g, '$1[name]')
|
|
327
|
-
.replace(/[-_.]\[hash(?::\d+)?\]/g, '')
|
|
328
|
-
.replace(/\[hash(?::\d+)?\]/g, '[name]');
|
|
329
|
-
}
|
|
330
|
-
function disableViteOxcSourcemap(pluginOption) {
|
|
331
|
-
if (Array.isArray(pluginOption)) {
|
|
332
|
-
pluginOption.forEach(disableViteOxcSourcemap);
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
if (!pluginOption || typeof pluginOption !== 'object') {
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
const plugin = pluginOption;
|
|
339
|
-
if (plugin.name === 'builtin:vite-transform' && plugin._options?.transformOptions) {
|
|
340
|
-
plugin._options.transformOptions.sourcemap = false;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
function createViteReporter(server) {
|
|
344
|
-
const { build, logger, root } = server.config;
|
|
345
|
-
return viteReporterPlugin({
|
|
346
|
-
assetsDir: path.join(build.assetsDir, '/'),
|
|
347
|
-
chunkLimit: 2000,
|
|
348
|
-
isLib: Boolean(build.lib),
|
|
349
|
-
isTty: Boolean(process.stdout.isTTY && !process.env.CI),
|
|
350
|
-
logInfo: (message) => logger.info(message),
|
|
351
|
-
reportCompressedSize: false,
|
|
352
|
-
root,
|
|
353
|
-
warnLargeChunks: false
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
243
|
function getBundledDev(server) {
|
|
357
244
|
const bundledDev = server.environments.client.bundledDev;
|
|
358
245
|
if (!bundledDev) {
|
|
@@ -17,12 +17,11 @@ export declare function renderHmrInfo(info: HmrInfo): string;
|
|
|
17
17
|
/** Provides a valid dependency before the host has a patch range to publish. */
|
|
18
18
|
export declare function renderInitialHmrPatches(): string;
|
|
19
19
|
/**
|
|
20
|
-
* Renders the
|
|
20
|
+
* Renders the cumulative patch suffix as inert CommonJS data.
|
|
21
21
|
*
|
|
22
|
-
* DevTools re-executes the Page because this
|
|
23
|
-
*
|
|
24
|
-
* so the Page's imports below the require resolve against the freshly registered modules.
|
|
22
|
+
* DevTools re-executes the Page because this dependency changed. The Page entry passes the exported payload to the persistent
|
|
23
|
+
* App runtime synchronously before importing its capsule, keeping delivery explicit and leaving this file free of side effects.
|
|
25
24
|
*/
|
|
26
25
|
export declare function renderHmrPatches(buildId: string, patches: readonly PatchUpdate[]): string;
|
|
27
|
-
/**
|
|
26
|
+
/** Atomically publishes one physical HMR module so DevTools can observe only complete JavaScript generations. */
|
|
28
27
|
export declare function writeHmrFile(outDir: string, fileName: string, source: string): Promise<void>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import fs from 'node:fs/promises';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
export const hmrInfoFileName = 'hmr/info.js';
|
|
@@ -13,22 +14,31 @@ export function renderInitialHmrPatches() {
|
|
|
13
14
|
return 'module.exports = undefined;\n';
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
|
-
* Renders the
|
|
17
|
+
* Renders the cumulative patch suffix as inert CommonJS data.
|
|
17
18
|
*
|
|
18
|
-
* DevTools re-executes the Page because this
|
|
19
|
-
*
|
|
20
|
-
* so the Page's imports below the require resolve against the freshly registered modules.
|
|
19
|
+
* DevTools re-executes the Page because this dependency changed. The Page entry passes the exported payload to the persistent
|
|
20
|
+
* App runtime synchronously before importing its capsule, keeping delivery explicit and leaving this file free of side effects.
|
|
21
21
|
*/
|
|
22
22
|
export function renderHmrPatches(buildId, patches) {
|
|
23
23
|
if (patches.length === 0) {
|
|
24
24
|
throw new Error('Cannot render an empty WX patch range.');
|
|
25
25
|
}
|
|
26
26
|
const rendered = patches.map((patch) => `{seq: ${patch.seq}, changedIds: ${JSON.stringify(patch.changedIds)}, factory: () => {\n${patch.code}\n}}`);
|
|
27
|
-
return `
|
|
27
|
+
return `module.exports = {buildId: ${JSON.stringify(buildId)}, patches: [${rendered.join(',')}]};\n`;
|
|
28
28
|
}
|
|
29
|
-
/**
|
|
29
|
+
/** Atomically publishes one physical HMR module so DevTools can observe only complete JavaScript generations. */
|
|
30
30
|
export async function writeHmrFile(outDir, fileName, source) {
|
|
31
31
|
const filePath = path.join(outDir, fileName);
|
|
32
|
-
|
|
33
|
-
await fs.
|
|
32
|
+
const directory = path.dirname(filePath);
|
|
33
|
+
await fs.mkdir(directory, { recursive: true });
|
|
34
|
+
const temporaryPath = path.join(directory, `.${path.basename(filePath)}.${randomUUID()}.txt`);
|
|
35
|
+
try {
|
|
36
|
+
// DevTools ignores the temporary .txt file; keeping it beside the destination guarantees a same-filesystem rename.
|
|
37
|
+
await fs.writeFile(temporaryPath, source);
|
|
38
|
+
await fs.rename(temporaryPath, filePath);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
// rename removes the source path on success; force cleanup covers interrupted writes and failed replacements.
|
|
42
|
+
await fs.rm(temporaryPath, { force: true });
|
|
43
|
+
}
|
|
34
44
|
}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import { type PatchUpdate } from './hmr-files.ts';
|
|
2
2
|
/** Abstracts the physical patches write; the engine owns the file destination. */
|
|
3
3
|
export type WritePatches = (content: string) => Promise<void>;
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Owns the cumulative sequence range between the host's published frontier and the runtime's applied frontier.
|
|
6
|
+
*
|
|
7
|
+
* For example, if the runtime has applied sequence 4:
|
|
8
|
+
*
|
|
9
|
+
* 1. produce([5, 6]) writes [5, 6];
|
|
10
|
+
* 2. produce([7]) before an application report writes [5, 6, 7], so DevTools may miss the first file event safely;
|
|
11
|
+
* 3. acknowledge(6) retains [7];
|
|
12
|
+
* 4. produce([8]) then writes [7, 8], exactly the suffix the runtime still needs.
|
|
13
|
+
*
|
|
14
|
+
* The host may advance Rolldown's published frontier through 8 immediately after those writes, but this history is pruned only
|
|
15
|
+
* through 6 until the runtime reports successful application. Published sequence and applied sequence are deliberately distinct.
|
|
16
|
+
*/
|
|
5
17
|
export declare class PatchPublisher {
|
|
6
18
|
private readonly writePatches;
|
|
7
19
|
private buildId;
|
|
8
|
-
/** Executable updates retained
|
|
20
|
+
/** Executable updates retained until the runtime confirms successful application. */
|
|
9
21
|
private readonly pendingPatches;
|
|
10
22
|
constructor(writePatches: WritePatches);
|
|
11
23
|
/** True when the buildId belongs to the current full build. */
|
|
@@ -15,8 +27,16 @@ export declare class PatchPublisher {
|
|
|
15
27
|
buildId: string;
|
|
16
28
|
previousBuildId: string | undefined;
|
|
17
29
|
}>;
|
|
18
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Appends a Rolldown batch and publishes the complete range not yet applied by the runtime.
|
|
32
|
+
*
|
|
33
|
+
* Resolution means the cumulative JavaScript generation is physically visible. The host may then advance Rolldown's
|
|
34
|
+
* published frontier immediately; pendingPatches remains intact until an application report proves the runtime caught up.
|
|
35
|
+
*/
|
|
19
36
|
produce(patches: readonly PatchUpdate[]): Promise<void>;
|
|
20
|
-
/**
|
|
21
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Removes only the prefix covered by the runtime's successful application frontier. Publication alone never calls this:
|
|
39
|
+
* retaining the gap between published and applied frontiers is what lets DevTools safely miss intermediate file events.
|
|
40
|
+
*/
|
|
41
|
+
acknowledge(seq: number): void;
|
|
22
42
|
}
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { renderHmrPatches } from './hmr-files.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Owns the cumulative sequence range between the host's published frontier and the runtime's applied frontier.
|
|
5
|
+
*
|
|
6
|
+
* For example, if the runtime has applied sequence 4:
|
|
7
|
+
*
|
|
8
|
+
* 1. produce([5, 6]) writes [5, 6];
|
|
9
|
+
* 2. produce([7]) before an application report writes [5, 6, 7], so DevTools may miss the first file event safely;
|
|
10
|
+
* 3. acknowledge(6) retains [7];
|
|
11
|
+
* 4. produce([8]) then writes [7, 8], exactly the suffix the runtime still needs.
|
|
12
|
+
*
|
|
13
|
+
* The host may advance Rolldown's published frontier through 8 immediately after those writes, but this history is pruned only
|
|
14
|
+
* through 6 until the runtime reports successful application. Published sequence and applied sequence are deliberately distinct.
|
|
15
|
+
*/
|
|
4
16
|
export class PatchPublisher {
|
|
5
17
|
writePatches;
|
|
6
18
|
buildId;
|
|
7
|
-
/** Executable updates retained
|
|
19
|
+
/** Executable updates retained until the runtime confirms successful application. */
|
|
8
20
|
pendingPatches = [];
|
|
9
21
|
// Explicit field assignment: node --test strips types and does not support parameter properties.
|
|
10
22
|
constructor(writePatches) {
|
|
@@ -22,19 +34,28 @@ export class PatchPublisher {
|
|
|
22
34
|
this.pendingPatches.length = 0;
|
|
23
35
|
return { buildId, previousBuildId };
|
|
24
36
|
}
|
|
25
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Appends a Rolldown batch and publishes the complete range not yet applied by the runtime.
|
|
39
|
+
*
|
|
40
|
+
* Resolution means the cumulative JavaScript generation is physically visible. The host may then advance Rolldown's
|
|
41
|
+
* published frontier immediately; pendingPatches remains intact until an application report proves the runtime caught up.
|
|
42
|
+
*/
|
|
26
43
|
async produce(patches) {
|
|
27
|
-
if (this.buildId === undefined || patches.length === 0)
|
|
44
|
+
if (this.buildId === undefined || patches.length === 0) {
|
|
28
45
|
return;
|
|
46
|
+
}
|
|
29
47
|
this.pendingPatches.push(...patches);
|
|
30
48
|
await this.writePatches(renderHmrPatches(this.buildId, this.pendingPatches));
|
|
31
49
|
}
|
|
32
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Removes only the prefix covered by the runtime's successful application frontier. Publication alone never calls this:
|
|
52
|
+
* retaining the gap between published and applied frontiers is what lets DevTools safely miss intermediate file events.
|
|
53
|
+
*/
|
|
33
54
|
acknowledge(seq) {
|
|
34
|
-
let
|
|
35
|
-
while (
|
|
36
|
-
|
|
55
|
+
let appliedCount = 0;
|
|
56
|
+
while (appliedCount < this.pendingPatches.length && this.pendingPatches[appliedCount].seq <= seq) {
|
|
57
|
+
appliedCount++;
|
|
37
58
|
}
|
|
38
|
-
|
|
59
|
+
this.pendingPatches.splice(0, appliedCount);
|
|
39
60
|
}
|
|
40
61
|
}
|
|
@@ -18,3 +18,15 @@ import type { Plugin } from 'vite';
|
|
|
18
18
|
* transformed by at most one of them, and modules outside all three never reach a handler.
|
|
19
19
|
*/
|
|
20
20
|
export declare function createWxReactRefreshTransforms(): Plugin[];
|
|
21
|
+
export declare function transformRefreshRuntime({ code, id }: {
|
|
22
|
+
code: string;
|
|
23
|
+
id: string;
|
|
24
|
+
}): import("../../../utils/transform.ts").AstTransformResult;
|
|
25
|
+
export declare function transformReactDevtoolsHook({ code, id }: {
|
|
26
|
+
code: string;
|
|
27
|
+
id: string;
|
|
28
|
+
}): import("../../../utils/transform.ts").AstTransformResult;
|
|
29
|
+
export declare function removeRefreshPreambleGuard({ code, id }: {
|
|
30
|
+
code: string;
|
|
31
|
+
id: string;
|
|
32
|
+
}): import("../../../utils/transform.ts").AstTransformResult;
|