@nasti-toolchain/nasti 2.5.0 → 2.5.2

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 (60) hide show
  1. package/README.md +34 -2
  2. package/dist/build-CENae4VM.js +748 -0
  3. package/dist/build-CENae4VM.js.map +1 -0
  4. package/dist/build-vjh6h7PB.cjs +786 -0
  5. package/dist/build-vjh6h7PB.cjs.map +1 -0
  6. package/dist/cli.cjs +166 -7608
  7. package/dist/cli.cjs.map +1 -1
  8. package/dist/cli.d.cts +1 -2
  9. package/dist/cli.d.ts +1 -2
  10. package/dist/cli.js +166 -7606
  11. package/dist/cli.js.map +1 -1
  12. package/dist/client/hmr.cjs +0 -19
  13. package/dist/client/hmr.d.cts +14 -12
  14. package/dist/client/hmr.d.cts.map +1 -0
  15. package/dist/client/hmr.d.ts +14 -12
  16. package/dist/client/hmr.d.ts.map +1 -0
  17. package/dist/client/hmr.js +1 -1
  18. package/dist/dev-engine-DJL9sfv5.cjs +473 -0
  19. package/dist/dev-engine-DJL9sfv5.cjs.map +1 -0
  20. package/dist/dev-engine-DapuTPIz.js +469 -0
  21. package/dist/dev-engine-DapuTPIz.js.map +1 -0
  22. package/dist/electron-Bj8H-BJ6.js +278 -0
  23. package/dist/electron-Bj8H-BJ6.js.map +1 -0
  24. package/dist/electron-D0PSXz-a.cjs +316 -0
  25. package/dist/electron-D0PSXz-a.cjs.map +1 -0
  26. package/dist/electron-dev-BzI2IFnR.cjs +272 -0
  27. package/dist/electron-dev-BzI2IFnR.cjs.map +1 -0
  28. package/dist/electron-dev-CM15gsFp.js +251 -0
  29. package/dist/electron-dev-CM15gsFp.js.map +1 -0
  30. package/dist/env-GY7K1zPK.js +833 -0
  31. package/dist/env-GY7K1zPK.js.map +1 -0
  32. package/dist/env-lOj3yw8v.cjs +924 -0
  33. package/dist/env-lOj3yw8v.cjs.map +1 -0
  34. package/dist/environment-B2jCrhY2.js +1361 -0
  35. package/dist/environment-B2jCrhY2.js.map +1 -0
  36. package/dist/environment-CjmvoHIG.cjs +1443 -0
  37. package/dist/environment-CjmvoHIG.cjs.map +1 -0
  38. package/dist/index.cjs +453 -7976
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +872 -817
  41. package/dist/index.d.cts.map +1 -0
  42. package/dist/index.d.ts +872 -817
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +418 -7939
  45. package/dist/index.js.map +1 -1
  46. package/dist/logger-BVZwdqqT.js +116 -0
  47. package/dist/logger-BVZwdqqT.js.map +1 -0
  48. package/dist/logger-M7AuVVTV.cjs +171 -0
  49. package/dist/logger-M7AuVVTV.cjs.map +1 -0
  50. package/dist/runnable-environment-BjoOvdDa.js +1345 -0
  51. package/dist/runnable-environment-BjoOvdDa.js.map +1 -0
  52. package/dist/runnable-environment-ByA3DHQ6.cjs +1351 -0
  53. package/dist/runnable-environment-ByA3DHQ6.cjs.map +1 -0
  54. package/dist/server-Bqgl25z0.js +1871 -0
  55. package/dist/server-Bqgl25z0.js.map +1 -0
  56. package/dist/server-BynlMdFc.cjs +1895 -0
  57. package/dist/server-BynlMdFc.cjs.map +1 -0
  58. package/package.json +20 -10
  59. package/dist/client/hmr.cjs.map +0 -1
  60. package/dist/client/hmr.js.map +0 -1
@@ -0,0 +1,469 @@
1
+ import { c as transformReactCode } from "./env-GY7K1zPK.js";
2
+ import { c as processHtml, l as readHtmlFile, p as createDebugger } from "./environment-B2jCrhY2.js";
3
+ import { a as resolveClientEntries, i as replaceEntryScript, o as toRolldownPlugins, r as getRolldownOptions } from "./build-CENae4VM.js";
4
+ import { r as getReactRefreshRuntimeEsm } from "./server-Bqgl25z0.js";
5
+ import path from "node:path";
6
+ import pc from "picocolors";
7
+ import crypto from "node:crypto";
8
+ import { WebSocketServer } from "ws";
9
+ //#region src/server/bundled/dev-engine.ts
10
+ const debug = createDebugger("nasti:bundled");
11
+ const MIME_TYPES = {
12
+ ".js": "application/javascript",
13
+ ".mjs": "application/javascript",
14
+ ".css": "text/css",
15
+ ".html": "text/html",
16
+ ".json": "application/json",
17
+ ".map": "application/json",
18
+ ".svg": "image/svg+xml",
19
+ ".png": "image/png",
20
+ ".jpg": "image/jpeg",
21
+ ".wasm": "application/wasm"
22
+ };
23
+ /** 内存产物存储(普通 Map,Vite MemoryFiles 同款做法) */
24
+ var MemoryFiles = class {
25
+ files = /* @__PURE__ */ new Map();
26
+ set(fileName, content) {
27
+ const etag = `"${crypto.createHash("sha1").update(content).digest("base64").slice(0, 27)}"`;
28
+ this.files.set(fileName, {
29
+ content,
30
+ etag
31
+ });
32
+ }
33
+ get(fileName) {
34
+ return this.files.get(fileName);
35
+ }
36
+ clear() {
37
+ this.files.clear();
38
+ }
39
+ };
40
+ async function createBundledDevServer(opts) {
41
+ const { config, clientEnv, httpServer } = opts;
42
+ const logger = config.logger;
43
+ let devFn;
44
+ let refreshWrapperFn = null;
45
+ try {
46
+ const experimental = await import("rolldown/experimental");
47
+ devFn = experimental.dev;
48
+ if (typeof devFn !== "function") throw new Error("dev() export missing");
49
+ if (typeof experimental.viteReactRefreshWrapperPlugin === "function") refreshWrapperFn = experimental.viteReactRefreshWrapperPlugin;
50
+ } catch (err) {
51
+ throw new Error(`[nasti] experimental.bundledDev requires rolldown's experimental dev() API (locked version is incompatible; got: ${err.message}). Remove --bundle / experimental.bundledDev to use the default unbundled dev server.`);
52
+ }
53
+ const html = await readHtmlFile(config.root, config.environments.client?.html);
54
+ const entryPoints = resolveClientEntries(config, html);
55
+ if (entryPoints.length === 0) throw new Error("No entry point found. Add a <script> tag to index.html or create src/main.ts");
56
+ const memoryFiles = new MemoryFiles();
57
+ const patches = new MemoryFiles();
58
+ /** entry facadeModuleId(绝对路径)→ 内存产物 fileName(assets/main.js) */
59
+ const entryFileNames = /* @__PURE__ */ new Map();
60
+ const bundledClients = /* @__PURE__ */ new Map();
61
+ const useReactRefresh = config.framework === "react" && config.server.hmr !== false && refreshWrapperFn != null;
62
+ const rolldownPlugins = [
63
+ ...useReactRefresh ? [createReactRefreshRuntimePlugin(entryPoints), createBundledOxcRefreshPlugin(config)] : [],
64
+ ...stripCatchAllLoad(toRolldownPlugins(clientEnv.plugins, clientEnv)),
65
+ ...useReactRefresh ? [refreshWrapperFn({
66
+ cwd: config.root,
67
+ include: [/\.[jt]sx(\?.*)?$/],
68
+ exclude: [/node_modules/],
69
+ jsxImportSource: "react",
70
+ reactRefreshHost: ""
71
+ })] : []
72
+ ];
73
+ const { inputOptions, outputOptions } = getRolldownOptions(clientEnv, entryPoints, rolldownPlugins);
74
+ let fullReloadTimer = null;
75
+ const scheduleFullReload = () => {
76
+ if (fullReloadTimer) clearTimeout(fullReloadTimer);
77
+ fullReloadTimer = setTimeout(() => {
78
+ logger.info(pc.green("page reload ") + pc.dim("(bundled)"), { timestamp: true });
79
+ broadcast({ type: "hmr:reload" });
80
+ }, 30);
81
+ };
82
+ const broadcast = (payload) => {
83
+ const data = JSON.stringify(payload);
84
+ for (const ws of bundledClients.values()) if (ws.readyState === 1) ws.send(data);
85
+ };
86
+ const sendTo = (clientId, payload) => {
87
+ const ws = bundledClients.get(clientId);
88
+ if (ws && ws.readyState === 1) ws.send(JSON.stringify(payload));
89
+ };
90
+ async function processUpdates(updates, changedFiles) {
91
+ let needsLatestOutput = false;
92
+ for (const { clientId, update } of updates) {
93
+ if (update.type === "Noop") continue;
94
+ if (update.type === "FullReload") {
95
+ debug?.(`full reload for ${clientId}: ${update.reason ?? ""}`);
96
+ needsLatestOutput = true;
97
+ continue;
98
+ }
99
+ const patchPath = `__nasti_patch/${update.filename}`;
100
+ patches.set(patchPath, update.code + "\n;globalThis.location?.reload();\n;export {}");
101
+ if (update.sourcemap && update.sourcemapFilename) patches.set(`__nasti_patch/${update.sourcemapFilename}`, update.sourcemap);
102
+ const url = `/${patchPath}`;
103
+ logger.info(pc.green("hmr update ") + pc.dim(changedFiles.map((f) => path.relative(config.root, f)).join(", ")), { timestamp: true });
104
+ sendTo(clientId, {
105
+ type: "hmr:update",
106
+ path: url,
107
+ url
108
+ });
109
+ }
110
+ if (needsLatestOutput) {
111
+ await engine.ensureLatestBuildOutput();
112
+ scheduleFullReload();
113
+ }
114
+ }
115
+ const engine = await devFn({
116
+ ...inputOptions,
117
+ cwd: config.root,
118
+ optimization: {
119
+ ...inputOptions.optimization,
120
+ inlineConst: false
121
+ },
122
+ experimental: {
123
+ ...inputOptions.experimental,
124
+ devMode: {
125
+ lazy: true,
126
+ host: config.server.host === true ? "localhost" : config.server.host || "localhost",
127
+ port: config.server.port
128
+ }
129
+ }
130
+ }, {
131
+ ...outputOptions,
132
+ entryFileNames: "assets/[name].js",
133
+ chunkFileNames: "assets/[name]-[hash].js",
134
+ minify: false,
135
+ sourcemap: true
136
+ }, {
137
+ watch: { skipWrite: true },
138
+ rebuildStrategy: "never",
139
+ onOutput(result) {
140
+ if (result instanceof Error) {
141
+ logger.error(pc.red(`[bundled] build error: ${result.message}`), { error: result });
142
+ return;
143
+ }
144
+ for (const file of result.output) {
145
+ const content = file.type === "chunk" ? file.code : file.source;
146
+ if (content != null) memoryFiles.set(file.fileName, content);
147
+ if (file.type === "chunk" && file.isEntry && file.facadeModuleId) entryFileNames.set(file.facadeModuleId, file.fileName);
148
+ if (file.type === "chunk" && file.map) memoryFiles.set(`${file.fileName}.map`, JSON.stringify(file.map));
149
+ }
150
+ debug?.(`bundle output refreshed (${result.output.length} files)`);
151
+ },
152
+ onAdditionalAssets(result) {
153
+ for (const file of result.output) {
154
+ const content = file.type === "chunk" ? file.code : file.source;
155
+ if (content != null) memoryFiles.set(file.fileName, content);
156
+ }
157
+ },
158
+ async onHmrUpdates(result) {
159
+ if (result instanceof Error) {
160
+ logger.error(pc.red(`[bundled] hmr error: ${result.message}`), { error: result });
161
+ broadcast({
162
+ type: "error",
163
+ err: {
164
+ message: result.message,
165
+ stack: result.stack
166
+ }
167
+ });
168
+ return;
169
+ }
170
+ const { updates, changedFiles } = result;
171
+ debug?.(`onHmrUpdates(engine watcher): ${changedFiles.length} changed, ${updates.length} updates`);
172
+ if (changedFiles.length === 0) return;
173
+ await processUpdates(updates, changedFiles);
174
+ }
175
+ });
176
+ await engine.run();
177
+ await engine.ensureCurrentBuildFinish();
178
+ logger.info(pc.dim(` bundled dev engine ready (${entryPoints.length} entries, in-memory)`));
179
+ const wss = new WebSocketServer({ noServer: true });
180
+ httpServer.on("upgrade", (req, socket, head) => {
181
+ if (req.headers["sec-websocket-protocol"] === "nasti-hmr") return;
182
+ const clientId = new URL(req.url ?? "/", "http://localhost").searchParams.get("clientId");
183
+ if (!clientId) return;
184
+ wss.handleUpgrade(req, socket, head, (ws) => {
185
+ bundledClients.set(clientId, ws);
186
+ debug?.(`bundled client connected: ${clientId}`);
187
+ engine.registerClient(clientId).then(async () => {
188
+ for (const fileName of entryFileNames.values()) await engine.notifyPayloadDelivered(fileName);
189
+ ws.send(JSON.stringify({ type: "connected" }));
190
+ }).catch((err) => {
191
+ debug?.(`registerClient failed for ${clientId}: ${err?.message ?? err}`);
192
+ ws.close();
193
+ });
194
+ ws.on("message", async (raw) => {
195
+ try {
196
+ if (JSON.parse(String(raw)).type === "hmr:invalidate") scheduleFullReload();
197
+ } catch (err) {
198
+ debug?.(`bundled ws message error: ${err.message}`);
199
+ }
200
+ });
201
+ ws.on("close", () => {
202
+ bundledClients.delete(clientId);
203
+ engine.removeClient(clientId).catch((err) => debug?.(`removeClient failed for ${clientId}: ${err?.message ?? err}`));
204
+ });
205
+ });
206
+ });
207
+ const middleware = (req, res, next) => {
208
+ (async () => {
209
+ const rawUrl = req.url ?? "/";
210
+ const url = new URL(rawUrl, "http://localhost");
211
+ const pathname = decodeURIComponent(url.pathname);
212
+ if (pathname === "/@vite/lazy" || pathname === "/@nasti/lazy") {
213
+ const id = url.searchParams.get("id");
214
+ const clientId = url.searchParams.get("clientId");
215
+ if (!id || !clientId) {
216
+ res.statusCode = 400;
217
+ res.end("// [nasti] lazy endpoint requires id & clientId");
218
+ return;
219
+ }
220
+ const output = await engine.compileEntry(id, clientId);
221
+ if (output.sourcemap && output.sourcemapFilename) memoryFiles.set(output.sourcemapFilename, output.sourcemap);
222
+ res.once("finish", () => {
223
+ engine.notifyPayloadDelivered(output.filename).catch((err) => debug?.(`notifyPayloadDelivered failed: ${err?.message ?? err}`));
224
+ });
225
+ res.setHeader("Content-Type", "application/javascript");
226
+ res.setHeader("Cache-Control", "no-store");
227
+ res.end(output.code + "\n;export {}");
228
+ return;
229
+ }
230
+ const patchHit = patches.get(pathname.replace(/^\//, ""));
231
+ if (patchHit) {
232
+ res.setHeader("Content-Type", "application/javascript");
233
+ res.setHeader("Cache-Control", "no-store");
234
+ res.end(patchHit.content);
235
+ return;
236
+ }
237
+ const fileName = pathname.replace(/^\//, "");
238
+ const hit = memoryFiles.get(fileName);
239
+ if (hit) {
240
+ if (req.headers["if-none-match"] === hit.etag) {
241
+ res.statusCode = 304;
242
+ res.end();
243
+ return;
244
+ }
245
+ res.setHeader("ETag", hit.etag);
246
+ res.setHeader("Content-Type", MIME_TYPES[path.extname(fileName)] ?? "application/octet-stream");
247
+ res.setHeader("Cache-Control", "no-cache");
248
+ res.once("finish", () => {
249
+ engine.notifyPayloadDelivered(fileName).catch((err) => debug?.(`notifyPayloadDelivered failed: ${err?.message ?? err}`));
250
+ });
251
+ res.end(hit.content);
252
+ return;
253
+ }
254
+ if (pathname === "/" || pathname.endsWith(".html")) {
255
+ const rawHtml = await readHtmlFile(config.root, config.environments.client?.html);
256
+ if (rawHtml) {
257
+ res.setHeader("Content-Type", "text/html");
258
+ res.setHeader("Cache-Control", "no-store");
259
+ res.end(await renderBundledIndexHtml(rawHtml, config, entryFileNames));
260
+ return;
261
+ }
262
+ }
263
+ next();
264
+ })().catch((err) => {
265
+ config.logger.error(pc.red(`[bundled] ${err.message}`), { error: err });
266
+ res.statusCode = 500;
267
+ res.end(`[nasti bundled] ${err.message}`);
268
+ });
269
+ };
270
+ return {
271
+ middleware,
272
+ async close() {
273
+ if (fullReloadTimer) clearTimeout(fullReloadTimer);
274
+ wss.close();
275
+ await engine.close();
276
+ }
277
+ };
278
+ }
279
+ /**
280
+ * 剥掉 nasti:resolve 的文件 load 钩子(仅 bundled 模式)。
281
+ *
282
+ * DevEngine 的文件监听只注册**原生加载过**的模块路径 —— 模块一旦由
283
+ * 插件 load 提供内容,就不进 watch 列表。nasti:resolve 目前只包装 JSON,
284
+ * 但 Rolldown 原生加载已经覆盖这一能力;为确保这些文件仍由 DevEngine
285
+ * 原生读取和监听,bundled 模式下继续剥掉该钩子。
286
+ * 同理:用户插件的 catch-all load 也会让对应文件失去 HMR —— 文档已注记。
287
+ */
288
+ function stripCatchAllLoad(plugins) {
289
+ return plugins.map((p) => p?.name === "nasti:resolve" ? {
290
+ ...p,
291
+ load: void 0
292
+ } : p);
293
+ }
294
+ /** 入口 preamble 的虚拟模块 specifier(resolveId 映射到 \0 前缀 id) */
295
+ const PREAMBLE_SPEC = "nasti:react-preamble";
296
+ const RESOLVED_PREAMBLE_ID = "\0nasti:react-preamble";
297
+ const REFRESH_RUNTIME_URL = "/@react-refresh";
298
+ /**
299
+ * 安装 Fast Refresh 全局钩子的 preamble —— 作为 bundle 内模块(而非 HTML 内联
300
+ * script)注入为入口的第一个 import:与 wrapper 产出的 import 共享**同一个**
301
+ * runtime 实例(HTML 路径会经 unbundled 中间件加载出第二个实例,注册表分裂),
302
+ * 且 scope-hoist 后先于 react-dom 执行(renderer 必须注册到已安装的 hook 上)。
303
+ */
304
+ const BUNDLED_PREAMBLE_CODE = `
305
+ import __rt from ${JSON.stringify(REFRESH_RUNTIME_URL)};
306
+ __rt.injectIntoGlobalHook(window);
307
+ window.$RefreshReg$ = () => {};
308
+ window.$RefreshSig$ = () => (type) => type;
309
+ window.__vite_plugin_react_preamble_installed__ = true;
310
+ `;
311
+ /**
312
+ * 原生 wrapper 的运行时契约扩展(plugin-react refresh-runtime 的同款辅助 API,
313
+ * react-refresh 本体没有):registerExportsForReactRefresh /
314
+ * validateRefreshBoundaryAndEnqueueUpdate / __hmr_import。
315
+ * 实现基于 getReactRefreshRuntimeEsm 已 re-export 的公共 API。
316
+ */
317
+ const WRAPPER_RUNTIME_HELPERS = `
318
+ // ── viteReactRefreshWrapperPlugin 运行时契约(@vitejs/plugin-react 同款)──
319
+ function __isPlainObject(obj) {
320
+ return Object.prototype.toString.call(obj) === '[object Object]' &&
321
+ (obj.constructor === Object || obj.constructor === undefined);
322
+ }
323
+ function __isCompoundComponent(type) {
324
+ if (!__isPlainObject(type)) return false;
325
+ for (const key in type) {
326
+ if (!isLikelyComponentType(type[key])) return false;
327
+ }
328
+ return true;
329
+ }
330
+ export function registerExportsForReactRefresh(filename, moduleExports) {
331
+ for (const key in moduleExports) {
332
+ if (key === '__esModule') continue;
333
+ const exportValue = moduleExports[key];
334
+ if (isLikelyComponentType(exportValue)) {
335
+ register(exportValue, filename + ' export ' + key);
336
+ } else if (__isCompoundComponent(exportValue)) {
337
+ for (const subKey in exportValue) {
338
+ register(exportValue[subKey], filename + ' export ' + key + '-' + subKey);
339
+ }
340
+ }
341
+ }
342
+ }
343
+ let __enqueueTimer;
344
+ const __hooks = [];
345
+ window.__registerBeforePerformReactRefresh = (cb) => { __hooks.push(cb); };
346
+ function __enqueueUpdate() {
347
+ clearTimeout(__enqueueTimer);
348
+ __enqueueTimer = setTimeout(async () => {
349
+ if (__hooks.length) await Promise.all(__hooks.map((cb) => cb()));
350
+ performReactRefresh();
351
+ }, 16);
352
+ }
353
+ function __predicateOnExport(ignoredExports, moduleExports, predicate) {
354
+ for (const key in moduleExports) {
355
+ if (ignoredExports.includes(key)) continue;
356
+ if (!predicate(key, moduleExports[key])) return key;
357
+ }
358
+ return true;
359
+ }
360
+ export function validateRefreshBoundaryAndEnqueueUpdate(id, prevExports, nextExports) {
361
+ const ignoredExports = window.__getReactRefreshIgnoredExports?.({ id }) ?? [];
362
+ if (__predicateOnExport(ignoredExports, prevExports, (key) => key in nextExports) !== true) {
363
+ return 'Could not Fast Refresh (export removed)';
364
+ }
365
+ if (__predicateOnExport(ignoredExports, nextExports, (key) => key in prevExports) !== true) {
366
+ return 'Could not Fast Refresh (new export)';
367
+ }
368
+ let hasExports = false;
369
+ const allExportsAreComponentsOrUnchanged = __predicateOnExport(
370
+ ignoredExports,
371
+ nextExports,
372
+ (key, value) => {
373
+ hasExports = true;
374
+ if (isLikelyComponentType(value)) return true;
375
+ if (__isCompoundComponent(value)) return true;
376
+ return prevExports[key] === nextExports[key];
377
+ },
378
+ );
379
+ if (hasExports && allExportsAreComponentsOrUnchanged === true) {
380
+ __enqueueUpdate();
381
+ } else {
382
+ return 'Could not Fast Refresh ("' + allExportsAreComponentsOrUnchanged + '" export is incompatible)';
383
+ }
384
+ }
385
+ export const __hmr_import = (module) => import(/* @vite-ignore */ module);
386
+ `;
387
+ /**
388
+ * bundle 内的 refresh runtime 虚拟模块 + 入口 preamble 注入。
389
+ * `/@react-refresh` 保留**字面 id**(不映射 \0 前缀):原生 wrapper 插件自带
390
+ * 该 id 的 resolveId(原样返回且先于一切 JS resolveId 执行,版本 smoke test 验证),
391
+ * 因此只能在 load 阶段按字面 id 提供内容 —— 与 @vitejs/plugin-react 的
392
+ * `load: { filter: exactRegex('/@react-refresh') }` 同款做法。
393
+ */
394
+ function createReactRefreshRuntimePlugin(entryPoints) {
395
+ const entryIds = new Set(entryPoints.map((p) => path.resolve(p)));
396
+ return {
397
+ name: "nasti:bundled-react-refresh",
398
+ resolveId(source) {
399
+ if (source === REFRESH_RUNTIME_URL) return REFRESH_RUNTIME_URL;
400
+ if (source === PREAMBLE_SPEC) return RESOLVED_PREAMBLE_ID;
401
+ return null;
402
+ },
403
+ load(id) {
404
+ if (id === REFRESH_RUNTIME_URL) return {
405
+ code: getReactRefreshRuntimeEsm() + WRAPPER_RUNTIME_HELPERS,
406
+ moduleType: "js"
407
+ };
408
+ if (id === RESOLVED_PREAMBLE_ID) return {
409
+ code: BUNDLED_PREAMBLE_CODE,
410
+ moduleType: "js"
411
+ };
412
+ return null;
413
+ },
414
+ transform(code, id) {
415
+ if (!entryIds.has(path.resolve(id.split("?")[0]))) return null;
416
+ return {
417
+ code: `import ${JSON.stringify(PREAMBLE_SPEC)};\n${code}`,
418
+ map: null
419
+ };
420
+ }
421
+ };
422
+ }
423
+ /**
424
+ * .jsx/.tsx 的 OXC refresh 注册转换($RefreshReg$/$RefreshSig$ 插入)。
425
+ * 必须以 JS 插件形式跑在 wrapper **之前** —— input 级 `transform.jsx` 在插件链
426
+ * 之后才执行,wrapper 的内容过滤(`$RefreshReg$(`)会看不到而静默跳过
427
+ *(版本 smoke test 验证)。node_modules 交给 rolldown 原生转换。
428
+ */
429
+ function createBundledOxcRefreshPlugin(config) {
430
+ return {
431
+ name: "nasti:bundled-oxc-refresh",
432
+ async transform(code, id) {
433
+ const clean = id.split("?")[0];
434
+ const result = await transformReactCode(clean, code, {
435
+ react: config.react,
436
+ consumer: "client",
437
+ development: true,
438
+ reactRefresh: true,
439
+ sourcemap: true,
440
+ target: config.build.target,
441
+ onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`)
442
+ });
443
+ if (!result) return null;
444
+ return {
445
+ code: result.code,
446
+ map: result.map ? JSON.parse(result.map) : void 0
447
+ };
448
+ }
449
+ };
450
+ }
451
+ /** index.html:入口 script src 改写到内存产物 + 用户插件 transformIndexHtml。
452
+ * 跳过内置 nasti:html(它注入的 /@nasti/client 与 HTML preamble 都是
453
+ * unbundled 专属 —— bundled 的 HMR 客户端与 preamble 已在 bundle 内)。 */
454
+ async function renderBundledIndexHtml(html, config, entryFileNames) {
455
+ let processed = html;
456
+ for (const plugin of config.plugins) {
457
+ if (!plugin.transformIndexHtml || plugin.name === "nasti:html") continue;
458
+ const result = await plugin.transformIndexHtml(processed);
459
+ if (typeof result === "string") processed = result;
460
+ else if (result && "html" in result) processed = processHtml(result.html, result.tags);
461
+ else if (Array.isArray(result)) processed = processHtml(processed, result);
462
+ }
463
+ for (const [facadeModuleId, fileName] of entryFileNames) processed = replaceEntryScript(processed, facadeModuleId, fileName, config, config.environments.client?.html ?? "index.html", "/");
464
+ return processed;
465
+ }
466
+ //#endregion
467
+ export { createBundledDevServer };
468
+
469
+ //# sourceMappingURL=dev-engine-DapuTPIz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-engine-DapuTPIz.js","names":["WsServer"],"sources":["../src/server/bundled/dev-engine.ts"],"sourcesContent":["// 完整打包模式 / Full Bundle Mode(NASTI_2.0_PLAN.md §2.5,opt-in)\n//\n// `experimental.bundledDev`(CLI `--bundle`)把 unbundled 的 transform+sirv\n// 管线换成长驻 Rolldown DevEngine:整体打包 client 环境,产物存内存 Map\n//(**非 memfs**,避免 native / WASM binding 行为差异),经\n// memory-files 中间件服务;HMR 由 onHmrUpdates 的 Patch/FullReload 驱动。\n//\n// 客户端运行时:**复用 rolldown 内置的 DefaultDevRuntime**(不传\n// devMode.implement 会替换整个 runtime 模块,包括\n// __toESM 等 scope-hoist helper,自带成本远高于收益)。服务端讲它的协议:\n// server→client: {type:'connected'} / {type:'hmr:update', path, url} /\n// {type:'hmr:reload'}\n// client→server: {type:'hmr:invalidate', moduleId}\n// ws 连接带 ?clientId=<uuid>(runtime 生成,多 tab 各自独立)\n//\n// 懒编译端点:DevEngine 的 lazy stub 使用 `/@vite/lazy?id=&clientId=`\n//(Vite 专属约定)—— 按已安装版本编码,同时也接受 /@nasti/lazy。\n//\n// React Fast Refresh:复用 rolldown 原生 `viteReactRefreshWrapperPlugin`\n//(计划 §2.5 ✓核实)。契约(由版本锁定与 smoke test 保护):\n// 1. wrapper 在插件 transform 阶段看到 **已含 `$RefreshReg$(` 的代码**才激活\n// → refresh 注册转换必须在它之前的 JS 插件里完成(input 级 transform.jsx\n// 在插件链之后才跑,wrapper 看不到)—— createBundledOxcRefreshPlugin。\n// 2. wrapper 产出 `import * as RefreshRuntime from \"/@react-refresh\"` 并调用\n// registerExportsForReactRefresh / validateRefreshBoundaryAndEnqueueUpdate\n// (plugin-react 扩展 API,react-refresh 本体没有)→ 由\n// createReactRefreshRuntimePlugin 在 **bundle 内**提供该虚拟模块。\n// 3. wrapper 校验 `window.$RefreshReg$` → preamble 以 bundle 内虚拟模块的\n// 形式注入为各入口的**第一个 import**(不能走 HTML 内联 script——那会经\n// unbundled 中间件加载出第二个 runtime 实例,refresh 注册表分裂失效)。\n//\n// 仅 client 环境(同 Vite FullBundleDevEnvironment);unbundled 保持默认。\nimport path from 'node:path'\nimport crypto from 'node:crypto'\nimport { WebSocketServer as WsServer, type WebSocket } from 'ws'\nimport pc from 'picocolors'\nimport type { IncomingMessage, ServerResponse, Server as HttpServer } from 'node:http'\nimport type { BindingClientHmrUpdate } from 'rolldown/experimental'\nimport type { ResolvedConfig } from '../../types.js'\nimport type { NastiEnvironment } from '../../core/environment.js'\nimport {\n getRolldownOptions,\n replaceEntryScript,\n resolveClientEntries,\n toRolldownPlugins,\n} from '../../build/index.js'\nimport { readHtmlFile, processHtml } from '../../plugins/html.js'\nimport { transformReactCode } from '../../core/transformer.js'\nimport { getReactRefreshRuntimeEsm } from '../middleware.js'\nimport { createDebugger } from '../../core/debug.js'\n\nconst debug = createDebugger('nasti:bundled')\n\nconst MIME_TYPES: Record<string, string> = {\n '.js': 'application/javascript',\n '.mjs': 'application/javascript',\n '.css': 'text/css',\n '.html': 'text/html',\n '.json': 'application/json',\n '.map': 'application/json',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.wasm': 'application/wasm',\n}\n\n/** 内存产物存储(普通 Map,Vite MemoryFiles 同款做法) */\nclass MemoryFiles {\n private files = new Map<string, { content: string | Uint8Array; etag: string }>()\n\n set(fileName: string, content: string | Uint8Array): void {\n const etag = `\"${crypto.createHash('sha1').update(content).digest('base64').slice(0, 27)}\"`\n this.files.set(fileName, { content, etag })\n }\n\n get(fileName: string) {\n return this.files.get(fileName)\n }\n\n clear(): void {\n this.files.clear()\n }\n}\n\nexport interface BundledDevOptions {\n config: ResolvedConfig\n clientEnv: NastiEnvironment\n httpServer: HttpServer\n}\n\nexport interface BundledDevServer {\n /** connect 中间件:内存产物 + index.html + 懒编译端点 */\n middleware: (req: IncomingMessage, res: ServerResponse, next: (err?: unknown) => void) => void\n close: () => Promise<void>\n}\n\nexport async function createBundledDevServer(opts: BundledDevOptions): Promise<BundledDevServer> {\n const { config, clientEnv, httpServer } = opts\n const logger = config.logger\n\n // 实验 API 守卫:dev/DevEngine 无 semver 保护,导入失败给出明确指引\n let devFn: typeof import('rolldown/experimental').dev\n let refreshWrapperFn:\n | typeof import('rolldown/experimental').viteReactRefreshWrapperPlugin\n | null = null\n try {\n const experimental = await import('rolldown/experimental')\n devFn = experimental.dev\n if (typeof devFn !== 'function') throw new Error('dev() export missing')\n // wrapper 缺失只降级 Fast Refresh(HMR 退化为整页刷新),不阻塞 bundled dev\n if (typeof experimental.viteReactRefreshWrapperPlugin === 'function') {\n refreshWrapperFn = experimental.viteReactRefreshWrapperPlugin\n }\n } catch (err: any) {\n throw new Error(\n `[nasti] experimental.bundledDev requires rolldown's experimental dev() API ` +\n `(locked version is incompatible; got: ${err.message}). ` +\n `Remove --bundle / experimental.bundledDev to use the default unbundled dev server.`,\n )\n }\n\n const html = await readHtmlFile(config.root, config.environments.client?.html)\n const entryPoints = resolveClientEntries(config, html)\n if (entryPoints.length === 0) {\n throw new Error('No entry point found. Add a <script> tag to index.html or create src/main.ts')\n }\n\n const memoryFiles = new MemoryFiles()\n const patches = new MemoryFiles()\n /** entry facadeModuleId(绝对路径)→ 内存产物 fileName(assets/main.js) */\n const entryFileNames = new Map<string, string>()\n const bundledClients = new Map<string, WebSocket>()\n\n // 复用多环境 builder 的 per-env 选项装配(含插件转发表 / define / resolve)\n // React Fast Refresh 三件套(条件与 unbundled 中间件一致:framework !== 'vue'):\n // refresh runtime 虚拟模块 + 入口 preamble → OXC refresh 注册转换 →\n // Nasti 插件 → 原生 wrapper(必须在 OXC 之后才能看到 $RefreshReg$ 调用)\n const useReactRefresh =\n config.framework === 'react' && config.server.hmr !== false && refreshWrapperFn != null\n const rolldownPlugins = [\n ...(useReactRefresh\n ? [\n createReactRefreshRuntimePlugin(entryPoints),\n createBundledOxcRefreshPlugin(config),\n ]\n : []),\n ...stripCatchAllLoad(toRolldownPlugins(clientEnv.plugins, clientEnv)),\n ...(useReactRefresh\n ? [\n refreshWrapperFn!({\n cwd: config.root,\n include: [/\\.[jt]sx(\\?.*)?$/],\n exclude: [/node_modules/],\n jsxImportSource: 'react',\n reactRefreshHost: '',\n }),\n ]\n : []),\n ]\n const { inputOptions, outputOptions } = getRolldownOptions(clientEnv, entryPoints, rolldownPlugins)\n\n let fullReloadTimer: ReturnType<typeof setTimeout> | null = null\n const scheduleFullReload = () => {\n if (fullReloadTimer) clearTimeout(fullReloadTimer)\n fullReloadTimer = setTimeout(() => {\n logger.info(pc.green('page reload ') + pc.dim('(bundled)'), { timestamp: true })\n broadcast({ type: 'hmr:reload' })\n }, 30)\n }\n const broadcast = (payload: unknown) => {\n const data = JSON.stringify(payload)\n for (const ws of bundledClients.values()) {\n if (ws.readyState === 1) ws.send(data)\n }\n }\n const sendTo = (clientId: string, payload: unknown) => {\n const ws = bundledClients.get(clientId)\n if (ws && ws.readyState === 1) ws.send(JSON.stringify(payload))\n }\n\n // HMR 由 DevEngine 内置 watcher 独占驱动(onHmrUpdates)。\n // DevEngine 1.2+ 由内置 watcher 驱动,手动 invalidate API 已移除。\n async function processUpdates(\n updates: BindingClientHmrUpdate[],\n changedFiles: string[],\n ): Promise<void> {\n let needsLatestOutput = false\n for (const { clientId, update } of updates) {\n if (update.type === 'Noop') continue\n if (update.type === 'FullReload') {\n debug?.(`full reload for ${clientId}: ${update.reason ?? ''}`)\n needsLatestOutput = true\n continue\n }\n // Patch:存内存 + 通知该 client 拉取。浏览器完成 patch 求值后刷新页面,\n // 与 unbundled 客户端保持一致;尾部 export {} 同时用于 XSSI 加固。\n const patchPath = `__nasti_patch/${update.filename}`\n patches.set(\n patchPath,\n update.code + '\\n;globalThis.location?.reload();\\n;export {}',\n )\n if (update.sourcemap && update.sourcemapFilename) {\n patches.set(`__nasti_patch/${update.sourcemapFilename}`, update.sourcemap)\n }\n const url = `/${patchPath}`\n logger.info(\n pc.green('hmr update ') +\n pc.dim(changedFiles.map((f) => path.relative(config.root, f)).join(', ')),\n { timestamp: true },\n )\n sendTo(clientId, { type: 'hmr:update', path: url, url })\n }\n if (needsLatestOutput) {\n await engine.ensureLatestBuildOutput()\n scheduleFullReload()\n }\n }\n\n const engine = await devFn(\n {\n ...inputOptions,\n cwd: config.root,\n // Rolldown bug 规避:inlineConst 与 dev patch 机制冲突(vitejs/vite#21843)\n optimization: { ...(inputOptions as any).optimization, inlineConst: false },\n experimental: {\n ...(inputOptions as any).experimental,\n devMode: {\n lazy: true,\n // 默认 DevRuntime 把 ws 地址烤进 bundle:指向 Nasti dev server 本身\n //(端口被占自动 +1 时 HMR ws 会失联 —— 已知限制,产物仍可服务)\n host: config.server.host === true ? 'localhost' : (config.server.host as string) || 'localhost',\n port: config.server.port,\n },\n },\n } as any,\n {\n ...outputOptions,\n entryFileNames: 'assets/[name].js',\n chunkFileNames: 'assets/[name]-[hash].js',\n minify: false,\n sourcemap: true,\n },\n {\n watch: { skipWrite: true },\n rebuildStrategy: 'never',\n onOutput(result) {\n if (result instanceof Error) {\n logger.error(pc.red(`[bundled] build error: ${result.message}`), { error: result })\n return\n }\n for (const file of result.output) {\n const content = file.type === 'chunk' ? file.code : (file as any).source\n if (content != null) memoryFiles.set(file.fileName, content)\n if (file.type === 'chunk' && (file as any).isEntry && (file as any).facadeModuleId) {\n entryFileNames.set((file as any).facadeModuleId, file.fileName)\n }\n // sourcemap 同步进内存\n if (file.type === 'chunk' && (file as any).map) {\n memoryFiles.set(`${file.fileName}.map`, JSON.stringify((file as any).map))\n }\n }\n debug?.(`bundle output refreshed (${result.output.length} files)`)\n },\n onAdditionalAssets(result) {\n for (const file of result.output) {\n const content = file.type === 'chunk' ? file.code : (file as any).source\n if (content != null) memoryFiles.set(file.fileName, content)\n }\n },\n async onHmrUpdates(result) {\n if (result instanceof Error) {\n logger.error(pc.red(`[bundled] hmr error: ${result.message}`), { error: result })\n broadcast({ type: 'error', err: { message: result.message, stack: result.stack } })\n return\n }\n const { updates, changedFiles } = result\n debug?.(\n `onHmrUpdates(engine watcher): ${changedFiles.length} changed, ${updates.length} updates`,\n )\n if (changedFiles.length === 0) return\n await processUpdates(updates, changedFiles)\n },\n },\n )\n\n await engine.run()\n await engine.ensureCurrentBuildFinish()\n logger.info(pc.dim(` bundled dev engine ready (${entryPoints.length} entries, in-memory)`))\n\n // ── 打包模式专用 ws(默认 DevRuntime 不带子协议,带 ?clientId= 查询)────\n const wss = new WsServer({ noServer: true })\n httpServer.on('upgrade', (req, socket, head) => {\n if (req.headers['sec-websocket-protocol'] === 'nasti-hmr') return // unbundled 客户端\n const url = new URL(req.url ?? '/', 'http://localhost')\n const clientId = url.searchParams.get('clientId')\n if (!clientId) return\n wss.handleUpgrade(req, socket, head, (ws) => {\n bundledClients.set(clientId, ws)\n debug?.(`bundled client connected: ${clientId}`)\n void engine\n .registerClient(clientId)\n .then(async () => {\n // 入口响应在 runtime 建立 WS 前已经完成;连接后补记初始 payload。\n for (const fileName of entryFileNames.values()) {\n await engine.notifyPayloadDelivered(fileName)\n }\n ws.send(JSON.stringify({ type: 'connected' }))\n })\n .catch((err: any) => {\n debug?.(`registerClient failed for ${clientId}: ${err?.message ?? err}`)\n ws.close()\n })\n ws.on('message', async (raw) => {\n try {\n const msg = JSON.parse(String(raw))\n if (msg.type === 'hmr:invalidate') {\n // import.meta.hot.invalidate:保守处理为整页刷新\n scheduleFullReload()\n }\n } catch (err: any) {\n debug?.(`bundled ws message error: ${err.message}`)\n }\n })\n ws.on('close', () => {\n bundledClients.delete(clientId)\n engine\n .removeClient(clientId)\n .catch((err: any) => debug?.(`removeClient failed for ${clientId}: ${err?.message ?? err}`))\n })\n })\n })\n\n // ── 中间件:懒编译端点 + 内存产物 + index.html ─────────────────────────\n const middleware: BundledDevServer['middleware'] = (req, res, next) => {\n void (async () => {\n const rawUrl = req.url ?? '/'\n const url = new URL(rawUrl, 'http://localhost')\n const pathname = decodeURIComponent(url.pathname)\n\n // 懒编译(DevEngine stub 使用 /@vite/lazy;/@nasti/lazy 同义)\n if (pathname === '/@vite/lazy' || pathname === '/@nasti/lazy') {\n const id = url.searchParams.get('id')\n const clientId = url.searchParams.get('clientId')\n if (!id || !clientId) {\n res.statusCode = 400\n res.end('// [nasti] lazy endpoint requires id & clientId')\n return\n }\n const output = await engine.compileEntry(id, clientId)\n if (output.sourcemap && output.sourcemapFilename) {\n memoryFiles.set(output.sourcemapFilename, output.sourcemap)\n }\n res.once('finish', () => {\n void engine\n .notifyPayloadDelivered(output.filename)\n .catch((err: any) =>\n debug?.(`notifyPayloadDelivered failed: ${err?.message ?? err}`),\n )\n })\n res.setHeader('Content-Type', 'application/javascript')\n res.setHeader('Cache-Control', 'no-store')\n res.end(output.code + '\\n;export {}')\n return\n }\n\n // HMR patch\n const patchHit = patches.get(pathname.replace(/^\\//, ''))\n if (patchHit) {\n res.setHeader('Content-Type', 'application/javascript')\n res.setHeader('Cache-Control', 'no-store')\n res.end(patchHit.content)\n return\n }\n\n // 内存产物(ETag/304)\n const fileName = pathname.replace(/^\\//, '')\n const hit = memoryFiles.get(fileName)\n if (hit) {\n if (req.headers['if-none-match'] === hit.etag) {\n res.statusCode = 304\n res.end()\n return\n }\n res.setHeader('ETag', hit.etag)\n res.setHeader('Content-Type', MIME_TYPES[path.extname(fileName)] ?? 'application/octet-stream')\n res.setHeader('Cache-Control', 'no-cache')\n res.once('finish', () => {\n void engine\n .notifyPayloadDelivered(fileName)\n .catch((err: any) =>\n debug?.(`notifyPayloadDelivered failed: ${err?.message ?? err}`),\n )\n })\n res.end(hit.content)\n return\n }\n\n // index.html:注入 React preamble(unbundled 同款)+ 入口改写到内存产物\n if (pathname === '/' || pathname.endsWith('.html')) {\n const rawHtml = await readHtmlFile(config.root, config.environments.client?.html)\n if (rawHtml) {\n res.setHeader('Content-Type', 'text/html')\n res.setHeader('Cache-Control', 'no-store')\n res.end(await renderBundledIndexHtml(rawHtml, config, entryFileNames))\n return\n }\n }\n\n next()\n })().catch((err) => {\n config.logger.error(pc.red(`[bundled] ${err.message}`), { error: err })\n res.statusCode = 500\n res.end(`[nasti bundled] ${err.message}`)\n })\n }\n\n return {\n middleware,\n async close() {\n if (fullReloadTimer) clearTimeout(fullReloadTimer)\n wss.close()\n await engine.close()\n },\n }\n}\n\n// ── React Fast Refresh(bundled 模式三件套)────────────────────────────────\n\n/**\n * 剥掉 nasti:resolve 的文件 load 钩子(仅 bundled 模式)。\n *\n * DevEngine 的文件监听只注册**原生加载过**的模块路径 —— 模块一旦由\n * 插件 load 提供内容,就不进 watch 列表。nasti:resolve 目前只包装 JSON,\n * 但 Rolldown 原生加载已经覆盖这一能力;为确保这些文件仍由 DevEngine\n * 原生读取和监听,bundled 模式下继续剥掉该钩子。\n * 同理:用户插件的 catch-all load 也会让对应文件失去 HMR —— 文档已注记。\n */\nfunction stripCatchAllLoad(plugins: unknown[]): unknown[] {\n return plugins.map((p: any) =>\n p?.name === 'nasti:resolve' ? { ...p, load: undefined } : p,\n )\n}\n\n/** 入口 preamble 的虚拟模块 specifier(resolveId 映射到 \\0 前缀 id) */\nconst PREAMBLE_SPEC = 'nasti:react-preamble'\nconst RESOLVED_PREAMBLE_ID = '\\0nasti:react-preamble'\nconst REFRESH_RUNTIME_URL = '/@react-refresh'\n\n/**\n * 安装 Fast Refresh 全局钩子的 preamble —— 作为 bundle 内模块(而非 HTML 内联\n * script)注入为入口的第一个 import:与 wrapper 产出的 import 共享**同一个**\n * runtime 实例(HTML 路径会经 unbundled 中间件加载出第二个实例,注册表分裂),\n * 且 scope-hoist 后先于 react-dom 执行(renderer 必须注册到已安装的 hook 上)。\n */\nconst BUNDLED_PREAMBLE_CODE = `\nimport __rt from ${JSON.stringify(REFRESH_RUNTIME_URL)};\n__rt.injectIntoGlobalHook(window);\nwindow.$RefreshReg$ = () => {};\nwindow.$RefreshSig$ = () => (type) => type;\nwindow.__vite_plugin_react_preamble_installed__ = true;\n`\n\n/**\n * 原生 wrapper 的运行时契约扩展(plugin-react refresh-runtime 的同款辅助 API,\n * react-refresh 本体没有):registerExportsForReactRefresh /\n * validateRefreshBoundaryAndEnqueueUpdate / __hmr_import。\n * 实现基于 getReactRefreshRuntimeEsm 已 re-export 的公共 API。\n */\nconst WRAPPER_RUNTIME_HELPERS = `\n// ── viteReactRefreshWrapperPlugin 运行时契约(@vitejs/plugin-react 同款)──\nfunction __isPlainObject(obj) {\n return Object.prototype.toString.call(obj) === '[object Object]' &&\n (obj.constructor === Object || obj.constructor === undefined);\n}\nfunction __isCompoundComponent(type) {\n if (!__isPlainObject(type)) return false;\n for (const key in type) {\n if (!isLikelyComponentType(type[key])) return false;\n }\n return true;\n}\nexport function registerExportsForReactRefresh(filename, moduleExports) {\n for (const key in moduleExports) {\n if (key === '__esModule') continue;\n const exportValue = moduleExports[key];\n if (isLikelyComponentType(exportValue)) {\n register(exportValue, filename + ' export ' + key);\n } else if (__isCompoundComponent(exportValue)) {\n for (const subKey in exportValue) {\n register(exportValue[subKey], filename + ' export ' + key + '-' + subKey);\n }\n }\n }\n}\nlet __enqueueTimer;\nconst __hooks = [];\nwindow.__registerBeforePerformReactRefresh = (cb) => { __hooks.push(cb); };\nfunction __enqueueUpdate() {\n clearTimeout(__enqueueTimer);\n __enqueueTimer = setTimeout(async () => {\n if (__hooks.length) await Promise.all(__hooks.map((cb) => cb()));\n performReactRefresh();\n }, 16);\n}\nfunction __predicateOnExport(ignoredExports, moduleExports, predicate) {\n for (const key in moduleExports) {\n if (ignoredExports.includes(key)) continue;\n if (!predicate(key, moduleExports[key])) return key;\n }\n return true;\n}\nexport function validateRefreshBoundaryAndEnqueueUpdate(id, prevExports, nextExports) {\n const ignoredExports = window.__getReactRefreshIgnoredExports?.({ id }) ?? [];\n if (__predicateOnExport(ignoredExports, prevExports, (key) => key in nextExports) !== true) {\n return 'Could not Fast Refresh (export removed)';\n }\n if (__predicateOnExport(ignoredExports, nextExports, (key) => key in prevExports) !== true) {\n return 'Could not Fast Refresh (new export)';\n }\n let hasExports = false;\n const allExportsAreComponentsOrUnchanged = __predicateOnExport(\n ignoredExports,\n nextExports,\n (key, value) => {\n hasExports = true;\n if (isLikelyComponentType(value)) return true;\n if (__isCompoundComponent(value)) return true;\n return prevExports[key] === nextExports[key];\n },\n );\n if (hasExports && allExportsAreComponentsOrUnchanged === true) {\n __enqueueUpdate();\n } else {\n return 'Could not Fast Refresh (\"' + allExportsAreComponentsOrUnchanged + '\" export is incompatible)';\n }\n}\nexport const __hmr_import = (module) => import(/* @vite-ignore */ module);\n`\n\n/**\n * bundle 内的 refresh runtime 虚拟模块 + 入口 preamble 注入。\n * `/@react-refresh` 保留**字面 id**(不映射 \\0 前缀):原生 wrapper 插件自带\n * 该 id 的 resolveId(原样返回且先于一切 JS resolveId 执行,版本 smoke test 验证),\n * 因此只能在 load 阶段按字面 id 提供内容 —— 与 @vitejs/plugin-react 的\n * `load: { filter: exactRegex('/@react-refresh') }` 同款做法。\n */\nfunction createReactRefreshRuntimePlugin(entryPoints: string[]) {\n const entryIds = new Set(entryPoints.map((p) => path.resolve(p)))\n return {\n name: 'nasti:bundled-react-refresh',\n resolveId(source: string) {\n // wrapper 不在场时(理论上不会,防御)也保证 preamble 的 import 可解析\n if (source === REFRESH_RUNTIME_URL) return REFRESH_RUNTIME_URL\n if (source === PREAMBLE_SPEC) return RESOLVED_PREAMBLE_ID\n return null\n },\n load(id: string) {\n if (id === REFRESH_RUNTIME_URL) {\n return { code: getReactRefreshRuntimeEsm() + WRAPPER_RUNTIME_HELPERS, moduleType: 'js' }\n }\n if (id === RESOLVED_PREAMBLE_ID) {\n return { code: BUNDLED_PREAMBLE_CODE, moduleType: 'js' }\n }\n return null\n },\n transform(code: string, id: string) {\n if (!entryIds.has(path.resolve(id.split('?')[0]))) return null\n return { code: `import ${JSON.stringify(PREAMBLE_SPEC)};\\n${code}`, map: null }\n },\n }\n}\n\n/**\n * .jsx/.tsx 的 OXC refresh 注册转换($RefreshReg$/$RefreshSig$ 插入)。\n * 必须以 JS 插件形式跑在 wrapper **之前** —— input 级 `transform.jsx` 在插件链\n * 之后才执行,wrapper 的内容过滤(`$RefreshReg$(`)会看不到而静默跳过\n *(版本 smoke test 验证)。node_modules 交给 rolldown 原生转换。\n */\nfunction createBundledOxcRefreshPlugin(config: ResolvedConfig) {\n return {\n name: 'nasti:bundled-oxc-refresh',\n async transform(code: string, id: string) {\n const clean = id.split('?')[0]\n const result = await transformReactCode(clean, code, {\n react: config.react,\n consumer: 'client',\n development: true,\n reactRefresh: true,\n sourcemap: true,\n target: config.build.target,\n onWarning: (message) => config.logger.warn(`[nasti:react] ${message}`),\n })\n if (!result) return null\n return { code: result.code, map: result.map ? JSON.parse(result.map) : undefined }\n },\n }\n}\n\n/** index.html:入口 script src 改写到内存产物 + 用户插件 transformIndexHtml。\n * 跳过内置 nasti:html(它注入的 /@nasti/client 与 HTML preamble 都是\n * unbundled 专属 —— bundled 的 HMR 客户端与 preamble 已在 bundle 内)。 */\nasync function renderBundledIndexHtml(\n html: string,\n config: ResolvedConfig,\n entryFileNames: Map<string, string>,\n): Promise<string> {\n let processed = html\n for (const plugin of config.plugins) {\n if (!plugin.transformIndexHtml || plugin.name === 'nasti:html') continue\n const result = await plugin.transformIndexHtml(processed)\n if (typeof result === 'string') {\n processed = result\n } else if (result && 'html' in result) {\n processed = processHtml(result.html, result.tags)\n } else if (Array.isArray(result)) {\n processed = processHtml(processed, result)\n }\n }\n // 入口 script src → 内存产物 URL\n for (const [facadeModuleId, fileName] of entryFileNames) {\n processed = replaceEntryScript(\n processed,\n facadeModuleId,\n fileName,\n config,\n config.environments.client?.html ?? 'index.html',\n '/',\n )\n }\n return processed\n}\n"],"mappings":"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"}