rollipop 1.0.0-alpha.21 → 1.0.0-alpha.22

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/commands.js CHANGED
@@ -15,7 +15,6 @@ import { createDevMiddleware } from "@react-native/dev-middleware";
15
15
  import Fastify from "fastify";
16
16
  import mitt from "mitt";
17
17
  import http from "node:http";
18
- import EventEmitter from "node:events";
19
18
  import * as rolldown from "@rollipop/rolldown";
20
19
  import { dev, rollipopReactNativePlugin, rollipopReactRefreshWrapperPlugin } from "@rollipop/rolldown/experimental";
21
20
  import crypto, { randomUUID } from "node:crypto";
@@ -40,6 +39,7 @@ import { asConst } from "json-schema-to-ts";
40
39
  import mime from "mime";
41
40
  import Ajv from "ajv";
42
41
  import { codeFrameColumns } from "@babel/code-frame";
42
+ import EventEmitter from "node:events";
43
43
  import * as ws from "ws";
44
44
  import * as c12 from "c12";
45
45
  //#region \0rolldown/runtime.js
@@ -785,30 +785,14 @@ function getGlobalVariables(dev, buildType) {
785
785
  }
786
786
  //#endregion
787
787
  //#region src/utils/config.ts
788
- function bindReporter(config, eventSource, onEvent) {
789
- const originalReporter = config.reporter;
790
- config.reporter = { update(event) {
791
- switch (event.type) {
792
- case "bundle_build_started":
793
- eventSource.emit("buildStart");
794
- break;
795
- case "bundle_build_done":
796
- eventSource.emit("buildDone");
797
- break;
798
- case "bundle_build_failed":
799
- eventSource.emit("buildFailed", event.error);
800
- break;
801
- case "transform":
802
- eventSource.emit("transform", event.id, event.totalModules, event.transformedModules);
803
- break;
804
- case "watch_change":
805
- eventSource.emit("watchChange", event.id);
806
- break;
807
- }
808
- originalReporter?.update(event);
788
+ function bindReporter(config, onEvent) {
789
+ const reporter = { update(event) {
809
790
  onEvent?.(event);
810
791
  } };
811
- return config;
792
+ return {
793
+ ...config,
794
+ reporter
795
+ };
812
796
  }
813
797
  function resolveHmrConfig(config) {
814
798
  if (config.mode !== "development") return null;
@@ -1147,6 +1131,8 @@ var ProgressBarStatusReporter = class {
1147
1131
  update(event) {
1148
1132
  switch (event.type) {
1149
1133
  case "bundle_build_started":
1134
+ this.progressBar.setCurrent(0);
1135
+ if (this.flags & ProgressFlags.FILE_CHANGED) this.progressBar.setTotal(0);
1150
1136
  this.flags |= ProgressFlags.BUILD_IN_PROGRESS;
1151
1137
  this.progressBar.start();
1152
1138
  this.renderManager.start();
@@ -1163,10 +1149,6 @@ var ProgressBarStatusReporter = class {
1163
1149
  break;
1164
1150
  case "transform":
1165
1151
  const { id, totalModules, transformedModules } = event;
1166
- if (this.flags & ProgressFlags.FILE_CHANGED) {
1167
- logger.debug("Transformed changed file", { id });
1168
- return;
1169
- }
1170
1152
  this.renderProgress(id, totalModules, transformedModules);
1171
1153
  break;
1172
1154
  case "watch_change":
@@ -1852,21 +1834,45 @@ function reporterPlugin(options) {
1852
1834
  let totalModules = initialTotalModules;
1853
1835
  let startedAt = 0;
1854
1836
  let transformedModules = 0;
1837
+ let cacheHitModules = 0;
1855
1838
  let unknownTotalModules = totalModules === 0;
1839
+ let rebuildPending = false;
1840
+ function getProcessedModules() {
1841
+ return transformedModules + cacheHitModules;
1842
+ }
1843
+ function reportProgress(id) {
1844
+ const processedModules = getProcessedModules();
1845
+ if (!unknownTotalModules && totalModules < processedModules) totalModules = processedModules;
1846
+ reporter?.update({
1847
+ type: "transform",
1848
+ id,
1849
+ totalModules: unknownTotalModules ? void 0 : totalModules,
1850
+ transformedModules: processedModules
1851
+ });
1852
+ }
1856
1853
  return {
1857
1854
  name: "rollipop:status",
1858
1855
  buildStart() {
1859
1856
  startedAt = performance.now();
1860
1857
  transformedModules = 0;
1858
+ cacheHitModules = 0;
1859
+ if (rebuildPending) {
1860
+ totalModules = 0;
1861
+ unknownTotalModules = false;
1862
+ rebuildPending = false;
1863
+ }
1861
1864
  reporter?.update({ type: "bundle_build_started" });
1862
1865
  },
1863
1866
  buildEnd(error) {
1864
1867
  const endedAt = performance.now();
1865
- if (transformedModules !== 0) totalModules = transformedModules;
1868
+ const processedModules = getProcessedModules();
1869
+ if (processedModules !== 0) totalModules = processedModules;
1866
1870
  unknownTotalModules = false;
1867
1871
  reporter?.update(error == null ? {
1868
1872
  type: "bundle_build_done",
1869
1873
  totalModules,
1874
+ transformedModules,
1875
+ cacheHitModules,
1870
1876
  duration: endedAt - startedAt
1871
1877
  } : {
1872
1878
  type: "bundle_build_failed",
@@ -1877,16 +1883,15 @@ function reporterPlugin(options) {
1877
1883
  order: "post",
1878
1884
  handler(_code, id) {
1879
1885
  ++transformedModules;
1880
- if (!unknownTotalModules && totalModules < transformedModules) totalModules = transformedModules;
1881
- reporter?.update({
1882
- type: "transform",
1883
- id,
1884
- totalModules: unknownTotalModules ? void 0 : totalModules,
1885
- transformedModules
1886
- });
1886
+ reportProgress(id);
1887
1887
  }
1888
1888
  },
1889
+ transformCacheHit(id) {
1890
+ ++cacheHitModules;
1891
+ reportProgress(id);
1892
+ },
1889
1893
  watchChange(id) {
1894
+ rebuildPending = true;
1890
1895
  reporter?.update({
1891
1896
  type: "watch_change",
1892
1897
  id
@@ -2301,7 +2306,7 @@ var FileSystemBundleStore = class {
2301
2306
  };
2302
2307
  //#endregion
2303
2308
  //#region src/server/bundler-pool.ts
2304
- var BundlerDevEngine = class extends EventEmitter {
2309
+ var BundlerDevEngine = class {
2305
2310
  initializeHandle;
2306
2311
  isHmrEnabled;
2307
2312
  _id;
@@ -2310,24 +2315,14 @@ var BundlerDevEngine = class extends EventEmitter {
2310
2315
  _devEngine = null;
2311
2316
  _state = "idle";
2312
2317
  _status = "idle";
2313
- constructor(options, config, buildOptions, onReporterEvent) {
2314
- super();
2318
+ constructor(options, config, buildOptions, eventBus) {
2315
2319
  this.options = options;
2316
2320
  this.config = config;
2317
2321
  this.buildOptions = buildOptions;
2318
- this.onReporterEvent = onReporterEvent;
2322
+ this.eventBus = eventBus;
2319
2323
  this._id = Bundler.createId(config, buildOptions);
2320
2324
  this.initializeHandle = taskHandler();
2321
2325
  this.isHmrEnabled = Boolean(buildOptions.dev && config.devMode.hmr);
2322
- this.on("buildStart", () => {
2323
- this._status = "building";
2324
- });
2325
- this.on("buildDone", () => {
2326
- this._status = "build-done";
2327
- });
2328
- this.on("buildFailed", () => {
2329
- this._status = "build-failed";
2330
- });
2331
2326
  this.initialize();
2332
2327
  }
2333
2328
  get id() {
@@ -2347,8 +2342,24 @@ var BundlerDevEngine = class extends EventEmitter {
2347
2342
  async initialize() {
2348
2343
  if (this._state !== "idle" || this._devEngine != null) return this;
2349
2344
  this._state = "initializing";
2350
- const onEvent = this.onReporterEvent ? (event) => this.onReporterEvent(this._id, event) : void 0;
2351
- const devEngine = await Bundler.devEngine(bindReporter(this.config, this, onEvent), this.buildOptions, {
2345
+ const config = bindReporter(this.config, (event) => {
2346
+ switch (event.type) {
2347
+ case "bundle_build_started":
2348
+ this._status = "building";
2349
+ break;
2350
+ case "bundle_build_done":
2351
+ this._status = "build-done";
2352
+ break;
2353
+ case "bundle_build_failed":
2354
+ this._status = "build-failed";
2355
+ break;
2356
+ }
2357
+ this.eventBus.emit({
2358
+ ...event,
2359
+ bundlerId: this.id
2360
+ });
2361
+ });
2362
+ const devEngine = await Bundler.devEngine(config, this.buildOptions, {
2352
2363
  host: this.options.server.host,
2353
2364
  port: this.options.server.port,
2354
2365
  onHmrUpdates: (errorOrResult) => {
@@ -2358,17 +2369,25 @@ var BundlerDevEngine = class extends EventEmitter {
2358
2369
  bundlerId: this.id,
2359
2370
  error: errorOrResult
2360
2371
  });
2361
- const normalizedError = normalizeRolldownError(errorOrResult);
2362
- this.config.reporter?.update({
2372
+ const event = {
2363
2373
  type: "bundle_build_failed",
2364
- error: normalizedError
2374
+ error: normalizeRolldownError(errorOrResult)
2375
+ };
2376
+ this._status = "build-failed";
2377
+ this.eventBus.emit({
2378
+ ...event,
2379
+ bundlerId: this.id
2365
2380
  });
2366
2381
  } else {
2367
2382
  logger$1.trace("Detected changed files", {
2368
2383
  bundlerId: this.id,
2369
2384
  changedFiles: errorOrResult.changedFiles
2370
2385
  });
2371
- this.emit("hmrUpdates", errorOrResult.updates);
2386
+ this.eventBus.emit({
2387
+ bundlerId: this.id,
2388
+ type: "hmr_updates",
2389
+ updates: errorOrResult.updates
2390
+ });
2372
2391
  }
2373
2392
  },
2374
2393
  onOutput: (errorOrResult) => {
@@ -2377,7 +2396,15 @@ var BundlerDevEngine = class extends EventEmitter {
2377
2396
  logger$1.trace("onOutput", { bundlerId: this.id });
2378
2397
  logger$1.error(errorOrResult.message);
2379
2398
  this.buildFailedError = normalizedError;
2380
- this.emit("buildFailed", normalizedError);
2399
+ const event = {
2400
+ type: "bundle_build_failed",
2401
+ error: normalizedError
2402
+ };
2403
+ this._status = "build-failed";
2404
+ this.eventBus.emit({
2405
+ ...event,
2406
+ bundlerId: this.id
2407
+ });
2381
2408
  } else {
2382
2409
  const output = errorOrResult.output[0];
2383
2410
  this.updateBundleStore(output);
@@ -2413,16 +2440,13 @@ var BundlerDevEngine = class extends EventEmitter {
2413
2440
  };
2414
2441
  var BundlerPool = class BundlerPool {
2415
2442
  static instances = /* @__PURE__ */ new Map();
2416
- constructor(config, resolvedServerOptions, onReporterEvent) {
2443
+ constructor(config, resolvedServerOptions, eventBus) {
2417
2444
  this.config = config;
2418
2445
  this.resolvedServerOptions = resolvedServerOptions;
2419
- this.onReporterEvent = onReporterEvent;
2420
- }
2421
- instanceKey(bundleName, buildOptions) {
2422
- return `${bundleName}-${Bundler.createId(this.config, buildOptions)}`;
2446
+ this.eventBus = eventBus;
2423
2447
  }
2424
2448
  get(bundleName, buildOptions) {
2425
- const key = this.instanceKey(getBaseBundleName(bundleName), buildOptions);
2449
+ const key = `${getBaseBundleName(bundleName)}-${Bundler.createId(this.config, buildOptions)}`;
2426
2450
  const instance = BundlerPool.instances.get(key);
2427
2451
  if (instance) return instance;
2428
2452
  else {
@@ -2430,16 +2454,14 @@ var BundlerPool = class BundlerPool {
2430
2454
  bundleName,
2431
2455
  key
2432
2456
  });
2433
- const instance = new BundlerDevEngine({ server: this.resolvedServerOptions }, this.config, buildOptions, this.onReporterEvent);
2457
+ const instance = new BundlerDevEngine({ server: this.resolvedServerOptions }, this.config, buildOptions, this.eventBus);
2434
2458
  logger$1.debug("Setting new bundler instance", { key });
2435
2459
  BundlerPool.instances.set(key, instance);
2436
2460
  return instance;
2437
2461
  }
2438
2462
  }
2439
2463
  /**
2440
- * Look up a cached bundler by its reporter-facing id (the same id carried
2441
- * in SSE events such as `bundle_build_done`). Returns `undefined` when no
2442
- * instance with that id has been created yet.
2464
+ * Look up a cached bundler by the id carried as `bundlerId` in events such as `bundle_build_done`. Returns `undefined` when no instance with that id has been created yet.
2443
2465
  */
2444
2466
  getInstanceById(id) {
2445
2467
  for (const instance of BundlerPool.instances.values()) if (instance.id === id) return instance;
@@ -2458,6 +2480,21 @@ function errorHandler(error, request, reply) {
2458
2480
  reply.status(500).send("Internal Server Error");
2459
2481
  }
2460
2482
  //#endregion
2483
+ //#region src/server/events/event-bus.ts
2484
+ var EventBus = class {
2485
+ listeners = /* @__PURE__ */ new Set();
2486
+ emit(event) {
2487
+ for (const listener of this.listeners) listener(event);
2488
+ }
2489
+ subscribe(listener) {
2490
+ this.listeners.add(listener);
2491
+ return () => {
2492
+ this.listeners.delete(listener);
2493
+ };
2494
+ }
2495
+ };
2496
+ var ServerEventBus = class extends EventBus {};
2497
+ //#endregion
2461
2498
  //#region src/utils/reset-cache.ts
2462
2499
  /**
2463
2500
  * Resolve the build cache directory for the given project root. The cache
@@ -2480,6 +2517,67 @@ function resetCache(projectRoot) {
2480
2517
  });
2481
2518
  }
2482
2519
  //#endregion
2520
+ //#region src/server/sse/adapter.ts
2521
+ function toSSEEvent(event) {
2522
+ switch (event.type) {
2523
+ case "client_log": return {
2524
+ type: "client_log",
2525
+ ...event.bundlerId != null ? { bundlerId: event.bundlerId } : {},
2526
+ data: event.data
2527
+ };
2528
+ case "device_connected": return {
2529
+ type: "device_connected",
2530
+ clientId: event.client.id
2531
+ };
2532
+ case "device_disconnected": return {
2533
+ type: "device_disconnected",
2534
+ clientId: event.client.id
2535
+ };
2536
+ case "server_ready":
2537
+ case "cache_reset": return event;
2538
+ case "bundle_build_started":
2539
+ case "bundle_build_done":
2540
+ case "bundle_build_failed":
2541
+ case "watch_change": return reporterEventToSSEEvent(event);
2542
+ case "hmr_updates":
2543
+ case "device_message":
2544
+ case "device_error":
2545
+ case "transform": return null;
2546
+ }
2547
+ }
2548
+ function reporterEventToSSEEvent(event) {
2549
+ switch (event.type) {
2550
+ case "bundle_build_started": return {
2551
+ type: "bundle_build_started",
2552
+ bundlerId: event.bundlerId
2553
+ };
2554
+ case "bundle_build_done": return {
2555
+ type: "bundle_build_done",
2556
+ bundlerId: event.bundlerId,
2557
+ totalModules: event.totalModules,
2558
+ transformedModules: event.transformedModules,
2559
+ cacheHitModules: event.cacheHitModules,
2560
+ duration: event.duration
2561
+ };
2562
+ case "bundle_build_failed": return {
2563
+ type: "bundle_build_failed",
2564
+ bundlerId: event.bundlerId,
2565
+ error: stripAnsi(event.error.message)
2566
+ };
2567
+ case "watch_change": return {
2568
+ type: "watch_change",
2569
+ bundlerId: event.bundlerId,
2570
+ file: event.id
2571
+ };
2572
+ case "client_log": return {
2573
+ type: "client_log",
2574
+ bundlerId: event.bundlerId,
2575
+ data: event.data
2576
+ };
2577
+ case "transform": return null;
2578
+ }
2579
+ }
2580
+ //#endregion
2483
2581
  //#region src/server/mcp/server.ts
2484
2582
  function createMcpServer(options) {
2485
2583
  const { projectRoot, eventBus } = options;
@@ -2500,11 +2598,14 @@ function createMcpServer(options) {
2500
2598
  });
2501
2599
  server.registerTool("get_build_events", {
2502
2600
  title: "Get Build Events",
2503
- description: "Subscribe to bundler events for a duration. Returns all events (build start/done/fail, watch changes, client logs, device connections) collected during the wait period.",
2601
+ description: "Subscribe to bundler events for a duration. Returns all events (build start/done/fail, watch changes, client logs, device connections) collected during the wait period. Bundler-scoped events include bundlerId.",
2504
2602
  inputSchema: { duration: z.number().min(1e3).max(6e4).default(1e4).describe("How long to listen for events in milliseconds (1000-60000, default 10000)") }
2505
2603
  }, async ({ duration }) => {
2506
2604
  const events = [];
2507
- const unsubscribe = eventBus.collect(events);
2605
+ const unsubscribe = eventBus.subscribe((event) => {
2606
+ const sseEvent = toSSEEvent(event);
2607
+ if (sseEvent != null) events.push(sseEvent);
2608
+ });
2508
2609
  await new Promise((resolve) => setTimeout(resolve, duration));
2509
2610
  unsubscribe();
2510
2611
  if (events.length === 0) return { content: [{
@@ -2761,6 +2862,11 @@ const bundleRequestSchema = asConst({
2761
2862
  });
2762
2863
  new Ajv().compile(bundleRequestSchema);
2763
2864
  //#endregion
2865
+ //#region src/server/events/types.ts
2866
+ function isBundlerEventForId(event, bundlerId) {
2867
+ return "bundlerId" in event && event.bundlerId === bundlerId;
2868
+ }
2869
+ //#endregion
2764
2870
  //#region src/server/middlewares/serve-bundle.ts
2765
2871
  const routeParamSchema = asConst({
2766
2872
  type: "object",
@@ -2772,7 +2878,7 @@ function withGetBundleErrorHandler(reply, task) {
2772
2878
  });
2773
2879
  }
2774
2880
  const plugin$2 = fp((fastify, options) => {
2775
- const { getBundler } = options;
2881
+ const { eventBus, getBundler } = options;
2776
2882
  const getBundleOptions = (buildOptions) => {
2777
2883
  return {
2778
2884
  platform: buildOptions.platform,
@@ -2796,11 +2902,10 @@ const plugin$2 = fp((fastify, options) => {
2796
2902
  const bundler = getBundler(params.name, buildOptions);
2797
2903
  if (accept?.includes("multipart/mixed") ?? false) {
2798
2904
  const bundleResponse = new BundleResponse(reply);
2799
- const transformHandler = (_id, totalModules = 0, transformedModules) => {
2800
- bundleResponse.writeBundleState(transformedModules, totalModules);
2801
- };
2802
- bundler.on("transform", transformHandler);
2803
- await bundler.getBundle().then((bundle) => bundleResponse.endWithBundle(bundle.code)).catch((error) => bundleResponse.endWithError(error)).finally(() => bundler.off("transform", transformHandler));
2905
+ const unsubscribe = eventBus.subscribe((event) => {
2906
+ if (isBundlerEventForId(event, bundler.id) && event.type === "transform") bundleResponse.writeBundleState(event.transformedModules, event.totalModules ?? 0);
2907
+ });
2908
+ await bundler.getBundle().then((bundle) => bundleResponse.endWithBundle(bundle.code)).catch((error) => bundleResponse.endWithError(error)).finally(unsubscribe);
2804
2909
  } else {
2805
2910
  this.log.debug(`client is not support multipart/mixed content: ${accept ?? "<empty>"}`);
2806
2911
  const code = (await withGetBundleErrorHandler(reply, bundler.getBundle())).code;
@@ -2828,7 +2933,7 @@ const plugin$2 = fp((fastify, options) => {
2828
2933
  }, { name: "serve-bundle" });
2829
2934
  //#endregion
2830
2935
  //#region src/server/middlewares/sse.ts
2831
- const plugin$1 = fp((fastify, { eventBus }) => {
2936
+ const plugin$1 = fp((fastify, { publisher }) => {
2832
2937
  fastify.get("/sse/events", (request, reply) => {
2833
2938
  const res = reply.raw;
2834
2939
  res.writeHead(200, {
@@ -2839,8 +2944,8 @@ const plugin$1 = fp((fastify, { eventBus }) => {
2839
2944
  });
2840
2945
  request.raw.socket.setNoDelay(true);
2841
2946
  res.write(":ok\n\n");
2842
- eventBus.addClient(res);
2843
- request.raw.on("close", () => eventBus.removeClient(res));
2947
+ publisher.addClient(res);
2948
+ request.raw.on("close", () => publisher.removeClient(res));
2844
2949
  });
2845
2950
  }, { name: "sse" });
2846
2951
  //#endregion
@@ -3016,14 +3121,12 @@ function printSymbolicateResult(rawStackFrame, symbolicateResult) {
3016
3121
  }
3017
3122
  //#endregion
3018
3123
  //#region src/server/sse/event-bus.ts
3019
- var SSEEventBus = class {
3124
+ var SSEEventPublisher = class {
3020
3125
  clients = /* @__PURE__ */ new Set();
3021
- listeners = /* @__PURE__ */ new Set();
3022
- emit(event) {
3126
+ publish(event) {
3023
3127
  const data = JSON.stringify(event);
3024
3128
  const message = `event: ${event.type}\ndata: ${data}\n\n`;
3025
3129
  for (const client of this.clients) if (!client.closed) client.write(message);
3026
- for (const listener of this.listeners) listener(event);
3027
3130
  }
3028
3131
  /**
3029
3132
  * Subscribe an SSE HTTP response client.
@@ -3037,51 +3140,11 @@ var SSEEventBus = class {
3037
3140
  removeClient(res) {
3038
3141
  this.clients.delete(res);
3039
3142
  }
3040
- /**
3041
- * Subscribe a listener that collects events into the given array.
3042
- * Returns an unsubscribe function.
3043
- */
3044
- collect(collector) {
3045
- const listener = (event) => collector.push(event);
3046
- this.listeners.add(listener);
3047
- return () => this.listeners.delete(listener);
3048
- }
3049
3143
  get clientCount() {
3050
3144
  return this.clients.size;
3051
3145
  }
3052
3146
  };
3053
3147
  //#endregion
3054
- //#region src/server/sse/reporter.ts
3055
- function toSSEEvent(id, event) {
3056
- switch (event.type) {
3057
- case "bundle_build_started": return {
3058
- type: "bundle_build_started",
3059
- id
3060
- };
3061
- case "bundle_build_done": return {
3062
- type: "bundle_build_done",
3063
- id,
3064
- totalModules: event.totalModules,
3065
- duration: event.duration
3066
- };
3067
- case "bundle_build_failed": return {
3068
- type: "bundle_build_failed",
3069
- id,
3070
- error: stripAnsi(event.error.message)
3071
- };
3072
- case "watch_change": return {
3073
- type: "watch_change",
3074
- id,
3075
- file: event.id
3076
- };
3077
- case "client_log": return {
3078
- type: "client_log",
3079
- data: event.data
3080
- };
3081
- case "transform": return null;
3082
- }
3083
- }
3084
- //#endregion
3085
3148
  //#region src/server/wss/server.ts
3086
3149
  var WebSocketServer = class extends EventEmitter {
3087
3150
  clientId = 0;
@@ -3150,13 +3213,13 @@ function getWebSocketUpgradeHandler(websocketEndpoints) {
3150
3213
  //#region src/server/wss/hmr-server.ts
3151
3214
  var HMRServer = class extends WebSocketServer {
3152
3215
  bundlerPool;
3153
- reportEvent;
3216
+ eventBus;
3154
3217
  instances = /* @__PURE__ */ new Map();
3155
3218
  bindings = /* @__PURE__ */ new Map();
3156
- constructor({ bundlerPool, reportEvent }) {
3219
+ constructor({ bundlerPool, eventBus }) {
3157
3220
  super("hmr", { noServer: true });
3158
3221
  this.bundlerPool = bundlerPool;
3159
- this.reportEvent = reportEvent;
3222
+ this.eventBus = eventBus;
3160
3223
  }
3161
3224
  parseClientMessage(data) {
3162
3225
  const parsedData = JSON.parse(this.rawDataToString(data));
@@ -3183,33 +3246,34 @@ var HMRServer = class extends WebSocketServer {
3183
3246
  }
3184
3247
  bindEvents(client, instance) {
3185
3248
  if (this.bindings.get(client.id) == null) {
3186
- const handleHmrUpdates = (updates) => {
3187
- this.handleUpdates(client, updates);
3188
- };
3189
- const handleWatchChange = () => {
3190
- this.send(client, JSON.stringify({ type: "hmr:update-start" }));
3191
- };
3192
- const handleBuildFailed = (error) => {
3193
- this.send(client, JSON.stringify({
3194
- type: "hmr:error",
3195
- payload: {
3196
- type: "BuildError",
3197
- errors: [{ description: error.message }],
3198
- message: error.message
3199
- }
3200
- }));
3201
- };
3202
- instance.addListener("hmrUpdates", handleHmrUpdates);
3203
- instance.addListener("watchChange", handleWatchChange);
3204
- instance.addListener("buildFailed", handleBuildFailed);
3205
- this.bindings.set(client.id, {
3206
- hmrUpdates: handleHmrUpdates,
3207
- watchChange: handleWatchChange,
3208
- buildFailed: handleBuildFailed
3249
+ const unsubscribe = this.eventBus.subscribe((event) => {
3250
+ if (!isBundlerEventForId(event, instance.id)) return;
3251
+ switch (event.type) {
3252
+ case "hmr_updates":
3253
+ this.handleUpdates(client, event.updates);
3254
+ break;
3255
+ case "watch_change":
3256
+ this.send(client, JSON.stringify({ type: "hmr:update-start" }));
3257
+ break;
3258
+ case "bundle_build_failed":
3259
+ this.sendBuildFailed(client, event.error);
3260
+ break;
3261
+ }
3209
3262
  });
3263
+ this.bindings.set(client.id, { unsubscribe });
3210
3264
  this.logger.trace(`HMR event binding established (clientId: ${client.id})`);
3211
3265
  }
3212
3266
  }
3267
+ sendBuildFailed(client, error) {
3268
+ this.send(client, JSON.stringify({
3269
+ type: "hmr:error",
3270
+ payload: {
3271
+ type: "BuildError",
3272
+ errors: [{ description: error.message }],
3273
+ message: error.message
3274
+ }
3275
+ }));
3276
+ }
3213
3277
  async handleModuleRegistered(client, modules) {
3214
3278
  try {
3215
3279
  const instance = this.instances.get(client.id);
@@ -3276,11 +3340,7 @@ var HMRServer = class extends WebSocketServer {
3276
3340
  this.logger.trace(`HMR client cleanup (clientId: ${client.id})`);
3277
3341
  const binding = this.bindings.get(client.id);
3278
3342
  const instance = this.instances.get(client.id);
3279
- if (binding != null && instance != null) {
3280
- instance.removeListener("hmrUpdates", binding.hmrUpdates);
3281
- instance.removeListener("watchChange", binding.watchChange);
3282
- instance.removeListener("buildFailed", binding.buildFailed);
3283
- }
3343
+ if (binding != null) binding.unsubscribe();
3284
3344
  if (instance != null) try {
3285
3345
  instance.devEngine.removeClient(String(client.id));
3286
3346
  } catch (error) {
@@ -3327,13 +3387,16 @@ var HMRServer = class extends WebSocketServer {
3327
3387
  case "hmr:invalidate":
3328
3388
  this.handleInvalidate(client, message.moduleId);
3329
3389
  break;
3330
- case "hmr:log":
3331
- this.reportEvent({
3390
+ case "hmr:log": {
3391
+ const instance = this.instances.get(client.id);
3392
+ this.eventBus.emit({
3332
3393
  type: "client_log",
3394
+ ...instance != null ? { bundlerId: instance.id } : {},
3333
3395
  level: message.level,
3334
3396
  data: message.data
3335
3397
  });
3336
3398
  break;
3399
+ }
3337
3400
  }
3338
3401
  }
3339
3402
  onConnection(client) {
@@ -3374,22 +3437,58 @@ async function createDevServer(config, options) {
3374
3437
  loggerInstance: new DevServerLogger(),
3375
3438
  disableRequestLogging: true
3376
3439
  });
3377
- const sseEventBus = new SSEEventBus();
3378
- const bundlerPool = new BundlerPool(config, {
3379
- host,
3380
- port
3381
- }, (id, event) => {
3382
- const sseEvent = toSSEEvent(id, event);
3383
- if (sseEvent) sseEventBus.emit(sseEvent);
3440
+ const eventBus = new ServerEventBus();
3441
+ const ssePublisher = new SSEEventPublisher();
3442
+ const reporter = config.reporter;
3443
+ eventBus.subscribe((event) => {
3444
+ const sseEvent = toSSEEvent(event);
3445
+ if (sseEvent != null) ssePublisher.publish(sseEvent);
3384
3446
  });
3385
- const getBundler = (bundleName, buildOptions) => {
3386
- return bundlerPool.get(bundleName, merge(options?.buildOptions ?? {}, buildOptions));
3387
- };
3388
3447
  const { middleware: communityMiddleware, websocketEndpoints: communityWebsocketEndpoints, messageSocketEndpoint: { server: messageServer, broadcast }, eventsSocketEndpoint: { server: eventsServer, reportEvent } } = createDevServerMiddleware({
3389
3448
  port,
3390
3449
  host,
3391
3450
  watchFolders: []
3392
3451
  });
3452
+ eventBus.subscribe((event) => {
3453
+ switch (event.type) {
3454
+ case "bundle_build_started":
3455
+ case "bundle_build_done":
3456
+ case "bundle_build_failed":
3457
+ case "transform":
3458
+ case "watch_change":
3459
+ reporter?.update(event);
3460
+ break;
3461
+ case "client_log":
3462
+ reportEvent?.(event);
3463
+ reporter?.update(event);
3464
+ break;
3465
+ case "device_connected":
3466
+ emitter.emit("device.connected", { client: event.client });
3467
+ break;
3468
+ case "device_message":
3469
+ emitter.emit("device.message", {
3470
+ client: event.client,
3471
+ data: event.data
3472
+ });
3473
+ break;
3474
+ case "device_error":
3475
+ emitter.emit("device.error", {
3476
+ client: event.client,
3477
+ error: event.error
3478
+ });
3479
+ break;
3480
+ case "device_disconnected":
3481
+ emitter.emit("device.disconnected", { client: event.client });
3482
+ break;
3483
+ }
3484
+ });
3485
+ const bundlerPool = new BundlerPool(config, {
3486
+ host,
3487
+ port
3488
+ }, eventBus);
3489
+ const getBundler = (bundleName, buildOptions) => {
3490
+ return bundlerPool.get(bundleName, merge(options?.buildOptions ?? {}, buildOptions));
3491
+ };
3393
3492
  const { middleware: devMiddleware, websocketEndpoints } = createDevMiddleware({
3394
3493
  serverBaseUrl,
3395
3494
  logger: {
@@ -3407,29 +3506,22 @@ async function createDevServer(config, options) {
3407
3506
  });
3408
3507
  const hmrServer = new HMRServer({
3409
3508
  bundlerPool,
3410
- reportEvent: (event) => {
3411
- reportEvent?.(event);
3412
- config.reporter?.update(event);
3413
- }
3414
- }).on("connection", (client) => {
3415
- emitter.emit("device.connected", { client });
3416
- sseEventBus.emit({
3417
- type: "device_connected",
3418
- clientId: client.id
3419
- });
3420
- }).on("message", (client, data) => emitter.emit("device.message", {
3509
+ eventBus
3510
+ }).on("connection", (client) => eventBus.emit({
3511
+ type: "device_connected",
3512
+ client
3513
+ })).on("message", (client, data) => eventBus.emit({
3514
+ type: "device_message",
3421
3515
  client,
3422
3516
  data
3423
- })).on("error", (client, error) => emitter.emit("device.error", {
3517
+ })).on("error", (client, error) => eventBus.emit({
3518
+ type: "device_error",
3424
3519
  client,
3425
3520
  error
3426
- })).on("close", (client) => {
3427
- emitter.emit("device.disconnected", { client });
3428
- sseEventBus.emit({
3429
- type: "device_disconnected",
3430
- clientId: client.id
3431
- });
3432
- });
3521
+ })).on("close", (client) => eventBus.emit({
3522
+ type: "device_disconnected",
3523
+ client
3524
+ }));
3433
3525
  await fastify.register(import("@fastify/middie"));
3434
3526
  const devServer = {
3435
3527
  ...emitter,
@@ -3454,13 +3546,16 @@ async function createDevServer(config, options) {
3454
3546
  })
3455
3547
  };
3456
3548
  const { invokePostConfigureServer } = await invokeConfigureServer(devServer, config.plugins ?? []);
3457
- fastify.use(requestLogger).use(communityMiddleware).use(devMiddleware).register(plugin$1, { eventBus: sseEventBus }).register(plugin$4, {
3549
+ fastify.use(requestLogger).use(communityMiddleware).use(devMiddleware).register(plugin$1, { publisher: ssePublisher }).register(plugin$4, {
3458
3550
  projectRoot,
3459
- eventBus: sseEventBus
3551
+ eventBus
3460
3552
  }).register(plugin$5, { bundlerPool }).register(plugin$6, {
3461
3553
  projectRoot,
3462
- eventBus: sseEventBus
3463
- }).register(plugin, { getBundler }).register(plugin$2, { getBundler }).register(plugin$3, {
3554
+ eventBus
3555
+ }).register(plugin, { getBundler }).register(plugin$2, {
3556
+ eventBus,
3557
+ getBundler
3558
+ }).register(plugin$3, {
3464
3559
  projectRoot,
3465
3560
  host,
3466
3561
  port,
@@ -3473,7 +3568,7 @@ async function createDevServer(config, options) {
3473
3568
  "/hot": hmrServer.server
3474
3569
  }));
3475
3570
  await invokePostConfigureServer();
3476
- sseEventBus.emit({
3571
+ eventBus.emit({
3477
3572
  type: "server_ready",
3478
3573
  host,
3479
3574
  port
@@ -3522,6 +3617,7 @@ function getAgentGuide(baseUrl = defaultBaseUrl) {
3522
3617
  " Event format:",
3523
3618
  " event: <event_type>",
3524
3619
  " data: <json_payload>",
3620
+ " bundler-scoped events include bundlerId",
3525
3621
  "",
3526
3622
  " Useful event types:",
3527
3623
  " bundle_build_started, bundle_build_done, bundle_build_failed",
@@ -3650,6 +3746,7 @@ function mergeConfig(baseConfig, ...overrideConfigs) {
3650
3746
  //#endregion
3651
3747
  //#region src/config/load-config.ts
3652
3748
  const CONFIG_FILE_NAME = "rollipop";
3749
+ const INTERNAL_PLUGIN_HOOKS = ["transformCacheHit"];
3653
3750
  async function loadConfig(options = {}) {
3654
3751
  const { cwd = process.cwd(), configFile, mode, context = {} } = options;
3655
3752
  const defaultConfig = await getDefaultConfig(cwd, mode);
@@ -3684,7 +3781,12 @@ async function flattenPluginOption(pluginOption) {
3684
3781
  const awaitedPluginOption = await pluginOption;
3685
3782
  if (Array.isArray(awaitedPluginOption)) return (await Promise.all(awaitedPluginOption.map(flattenPluginOption))).flat();
3686
3783
  if (awaitedPluginOption == null || awaitedPluginOption === false) return [];
3687
- return [awaitedPluginOption];
3784
+ return [stripInternalPluginHooks(awaitedPluginOption)];
3785
+ }
3786
+ function stripInternalPluginHooks(plugin) {
3787
+ const maybeInternalPlugin = plugin;
3788
+ if (!INTERNAL_PLUGIN_HOOKS.some((hook) => hook in maybeInternalPlugin)) return plugin;
3789
+ return omit(maybeInternalPlugin, INTERNAL_PLUGIN_HOOKS);
3688
3790
  }
3689
3791
  async function resolvePluginConfig(baseConfig, plugins) {
3690
3792
  let mergedConfig = omit(baseConfig, ["plugins"]);
@@ -3854,6 +3956,7 @@ const action = async function(options) {
3854
3956
  dev: options.dev,
3855
3957
  minify: options.minify,
3856
3958
  cache: options.cache,
3959
+ ...options.sourcemapOutput ? { sourcemap: true } : {},
3857
3960
  outfile: options.bundleOutput,
3858
3961
  sourcemapOutfile: options.sourcemapOutput,
3859
3962
  assetsDir: options.assetsDest