rollipop 1.0.0-alpha.21 → 1.0.0-alpha.23

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 (161) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/{chunk-DEq-mXcV.js → _virtual/_rolldown/runtime.js} +1 -1
  3. package/dist/commands.d.ts +2 -4
  4. package/dist/commands.js +10 -3957
  5. package/dist/common/code.js +21 -0
  6. package/dist/common/constants.js +5 -0
  7. package/dist/common/env.js +33 -0
  8. package/dist/common/logger.d.ts +34 -0
  9. package/dist/common/logger.js +82 -0
  10. package/dist/common/logo.js +54 -0
  11. package/dist/common/progress-bar.js +167 -0
  12. package/dist/common/transformer.js +13 -0
  13. package/dist/common/types.d.ts +10 -0
  14. package/dist/config/compose-override.js +18 -0
  15. package/dist/config/defaults.d.ts +74 -0
  16. package/dist/config/defaults.js +74 -0
  17. package/dist/config/define-config.d.ts +13 -0
  18. package/dist/config/define-config.js +6 -0
  19. package/dist/config/index.d.ts +5 -0
  20. package/dist/config/index.js +5 -0
  21. package/dist/config/load-config.d.ts +19 -0
  22. package/dist/config/load-config.js +73 -0
  23. package/dist/config/merge-config.d.ts +12 -0
  24. package/dist/config/merge-config.js +20 -0
  25. package/dist/config/types.d.ts +452 -0
  26. package/dist/constants.d.ts +35 -0
  27. package/dist/constants.js +146 -0
  28. package/dist/core/assets.d.ts +91 -0
  29. package/dist/core/assets.js +244 -0
  30. package/dist/core/bundler.d.ts +15 -0
  31. package/dist/core/bundler.js +80 -0
  32. package/dist/core/env.d.ts +11 -0
  33. package/dist/core/env.js +36 -0
  34. package/dist/core/fs/data.js +9 -0
  35. package/dist/core/fs/storage.d.ts +15 -0
  36. package/dist/core/fs/storage.js +31 -0
  37. package/dist/core/plugins/babel-plugin.d.ts +22 -0
  38. package/dist/core/plugins/babel-plugin.js +74 -0
  39. package/dist/core/plugins/context.d.ts +10 -0
  40. package/dist/core/plugins/context.js +24 -0
  41. package/dist/core/plugins/dev-server-plugin.d.ts +13 -0
  42. package/dist/core/plugins/dev-server-plugin.js +27 -0
  43. package/dist/core/plugins/index.d.ts +13 -0
  44. package/dist/core/plugins/index.js +18 -0
  45. package/dist/core/plugins/prelude-plugin.d.ts +10 -0
  46. package/dist/core/plugins/prelude-plugin.js +23 -0
  47. package/dist/core/plugins/react-native-plugin.d.ts +36 -0
  48. package/dist/core/plugins/react-native-plugin.js +81 -0
  49. package/dist/core/plugins/reporter-plugin.d.ts +11 -0
  50. package/dist/core/plugins/reporter-plugin.js +87 -0
  51. package/dist/core/plugins/shared/filters.js +5 -0
  52. package/dist/core/plugins/swc-plugin.d.ts +26 -0
  53. package/dist/core/plugins/swc-plugin.js +108 -0
  54. package/dist/core/plugins/types.d.ts +18 -0
  55. package/dist/core/plugins/utils/source.js +10 -0
  56. package/dist/core/plugins/utils/transform-utils.js +56 -0
  57. package/dist/core/rolldown.js +313 -0
  58. package/dist/core/settings.js +19 -0
  59. package/dist/core/types.d.ts +83 -0
  60. package/dist/filter.d.ts +1 -0
  61. package/dist/filter.js +2 -0
  62. package/dist/hmr-runtime.iife.js +5 -5
  63. package/dist/index.d.ts +24 -1221
  64. package/dist/index.js +19 -4029
  65. package/dist/internal/react-native.js +24 -0
  66. package/dist/logger.js +5 -0
  67. package/dist/node/cli-utils.d.ts +10 -0
  68. package/dist/node/cli-utils.js +28 -0
  69. package/dist/node/cli.d.ts +6 -0
  70. package/dist/node/cli.js +23 -0
  71. package/dist/node/commands/agent/action.js +91 -0
  72. package/dist/node/commands/agent/command.js +10 -0
  73. package/dist/node/commands/agent/index.js +2 -0
  74. package/dist/node/commands/bundle/action.js +33 -0
  75. package/dist/node/commands/bundle/command.js +96 -0
  76. package/dist/node/commands/bundle/index.js +2 -0
  77. package/dist/node/commands/start/action.js +37 -0
  78. package/dist/node/commands/start/command.js +93 -0
  79. package/dist/node/commands/start/debugger.js +79 -0
  80. package/dist/node/commands/start/index.js +2 -0
  81. package/dist/node/commands/start/setup-interactive-mode.d.ts +20 -0
  82. package/dist/node/commands/start/setup-interactive-mode.js +107 -0
  83. package/dist/node/constants.js +4 -0
  84. package/dist/node/logger.js +5 -0
  85. package/dist/node/types.d.ts +23 -0
  86. package/dist/node/utils.js +23 -0
  87. package/dist/package.js +4 -0
  88. package/dist/runtime.js +1 -1
  89. package/dist/server/bundle.d.ts +12 -0
  90. package/dist/server/bundle.js +55 -0
  91. package/dist/server/bundler-pool.d.ts +51 -0
  92. package/dist/server/bundler-pool.js +197 -0
  93. package/dist/server/common/schema.js +19 -0
  94. package/dist/server/constants.d.ts +6 -0
  95. package/dist/server/constants.js +6 -0
  96. package/dist/server/create-dev-server.d.ts +6 -0
  97. package/dist/server/create-dev-server.js +185 -0
  98. package/dist/server/error.js +9 -0
  99. package/dist/server/events/event-bus.d.ts +12 -0
  100. package/dist/server/events/event-bus.js +16 -0
  101. package/dist/server/events/types.d.ts +37 -0
  102. package/dist/server/events/types.js +6 -0
  103. package/dist/server/index.d.ts +3 -0
  104. package/dist/server/index.js +3 -0
  105. package/dist/server/logger.js +33 -0
  106. package/dist/server/mcp/context.js +14 -0
  107. package/dist/server/mcp/server.js +86 -0
  108. package/dist/server/mcp/tools/app-log-diagnostics.js +37 -0
  109. package/dist/server/mcp/tools/build-diagnostics.js +97 -0
  110. package/dist/server/mcp/tools/build-info.js +33 -0
  111. package/dist/server/mcp/tools/device-diagnostics.js +52 -0
  112. package/dist/server/mcp/tools/index.js +277 -0
  113. package/dist/server/middlewares/request-logger.js +15 -0
  114. package/dist/server/middlewares/serve-assets.js +49 -0
  115. package/dist/server/middlewares/serve-bundle.js +72 -0
  116. package/dist/server/middlewares/sse.js +34 -0
  117. package/dist/server/middlewares/symbolicate.js +71 -0
  118. package/dist/server/sse/adapter.js +74 -0
  119. package/dist/server/sse/event-bus.js +26 -0
  120. package/dist/server/symbolicate.js +93 -0
  121. package/dist/server/types.d.ts +125 -0
  122. package/dist/server/wss/hmr-server.js +209 -0
  123. package/dist/server/wss/server.d.ts +9 -0
  124. package/dist/server/wss/server.js +70 -0
  125. package/dist/{runtime.d.cts → types/hmr.d.ts} +1 -12
  126. package/dist/types.d.ts +78 -0
  127. package/dist/utils/babel.js +11 -0
  128. package/dist/utils/build-options.js +17 -0
  129. package/dist/utils/bundle.js +6 -0
  130. package/dist/utils/config.d.ts +5 -0
  131. package/dist/utils/config.js +32 -0
  132. package/dist/utils/dev-server.js +51 -0
  133. package/dist/utils/env.js +7 -0
  134. package/dist/utils/errors.js +9 -0
  135. package/dist/utils/hash.js +8 -0
  136. package/dist/utils/id.js +28 -0
  137. package/dist/utils/node-resolve.js +42 -0
  138. package/dist/utils/promise.js +15 -0
  139. package/dist/utils/reporters.js +120 -0
  140. package/dist/utils/reset-cache.d.ts +8 -0
  141. package/dist/utils/reset-cache.js +25 -0
  142. package/dist/utils/response.js +91 -0
  143. package/dist/utils/run-build.d.ts +8 -0
  144. package/dist/utils/run-build.js +7 -0
  145. package/dist/utils/run-server.d.ts +6 -0
  146. package/dist/utils/run-server.js +20 -0
  147. package/dist/utils/runtime-target.js +9 -0
  148. package/dist/utils/serialize.js +10 -0
  149. package/dist/utils/server.js +6 -0
  150. package/dist/utils/storage.js +6 -0
  151. package/dist/utils/string.js +6 -0
  152. package/dist/utils/swc.js +10 -0
  153. package/dist/utils/terminal.js +86 -0
  154. package/dist/utils/url.js +23 -0
  155. package/package.json +56 -68
  156. package/dist/commands.cjs +0 -4008
  157. package/dist/commands.d.cts +0 -5
  158. package/dist/pluginutils.d.ts +0 -1
  159. package/dist/pluginutils.js +0 -2
  160. package/dist/runtime.cjs +0 -34
  161. /package/dist/{chunk-DXpK5_cz.js → chunk-DJV587Yu.js} +0 -0
package/dist/commands.js CHANGED
@@ -1,3962 +1,15 @@
1
- import { createRequire } from "node:module";
2
- import "@commander-js/extra-typings";
3
- import { invariant, isNotNil, merge, mergeWith, noop, omit, pick, range, throttle } from "es-toolkit";
4
- import chalk from "chalk";
5
- import gradient from "gradient-string";
6
- import path from "node:path";
7
- import dayjs from "dayjs";
8
- import readline from "node:readline";
9
- import { ReadStream } from "node:tty";
10
- import { select } from "@inquirer/prompts";
11
- import fs from "node:fs";
12
- import url from "url";
13
- import { createDevServerMiddleware } from "@react-native-community/cli-server-api";
14
- import { createDevMiddleware } from "@react-native/dev-middleware";
15
- import Fastify from "fastify";
16
- import mitt from "mitt";
17
- import http from "node:http";
18
- import EventEmitter from "node:events";
19
- import * as rolldown from "@rollipop/rolldown";
20
- import { dev, rollipopReactNativePlugin, rollipopReactRefreshWrapperPlugin } from "@rollipop/rolldown/experimental";
21
- import crypto, { randomUUID } from "node:crypto";
22
- import "@node-rs/xxhash";
23
- import dedent from "dedent";
24
- import wrapAnsi from "wrap-ansi";
25
- import dotenv from "dotenv";
26
- import dotenvExpand from "dotenv-expand";
27
- import { exactRegex, exclude, id, include, or } from "@rollipop/rolldown-pluginutils";
28
- import transform from "fast-flow-transform";
29
- import { imageSize } from "image-size";
30
- import * as babel from "@babel/core";
31
- import * as swc from "@swc/core";
32
- import stripAnsi from "strip-ansi";
33
- import { SourceMapConsumer } from "source-map";
34
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
35
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
36
- import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
37
- import fp from "fastify-plugin";
38
- import { z } from "zod";
39
- import { asConst } from "json-schema-to-ts";
40
- import mime from "mime";
41
- import Ajv from "ajv";
42
- import { codeFrameColumns } from "@babel/code-frame";
43
- import * as ws from "ws";
44
- import * as c12 from "c12";
45
- //#region \0rolldown/runtime.js
46
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
47
- //#endregion
48
- //#region src/common/logo.ts
49
- const CANDY = `
50
- _....._
51
- .' _..._ '.
52
- / /\` __ \`\\ \\
53
- ; ; /\` \\ | ;
54
- | | | (_/ ; |
55
- ; ; \\_ _.' .;
56
- \\ '. \`\` _.'/
57
- '._\`"'"\`_.'`;
58
- const STICK = `
59
- / /\`\`
60
- / /
61
- /__/`;
62
- const DESCRIPTIONS = [
63
- "Rollipop",
64
- "Modern build toolkit for React Native",
65
- "Powered by Rolldown"
66
- ];
67
- const PRIMARY_COLOR = "#42A5F5";
68
- const BASE_GRADIENT_COLORS = [PRIMARY_COLOR, "#BBDEFB"];
69
- const PADDING = 20;
70
- function printLogo() {
71
- let maxLogoWidth = 0;
72
- const padding = " ".repeat(PADDING);
73
- const gradientColors = [...BASE_GRADIENT_COLORS];
74
- const styledCandy = CANDY.split("\n").map((line) => {
75
- maxLogoWidth = Math.max(line.length, maxLogoWidth);
76
- return gradient(gradientColors.reverse())(line);
77
- }).join("\n") + STICK;
78
- console.log(styledCandy.split("\n").map((line) => padding + line).join("\n"));
79
- console.log("");
80
- DESCRIPTIONS.forEach((description, index) => {
81
- const descriptionHalfLength = description.length / 2;
82
- const logoHalfWidth = maxLogoWidth / 2;
83
- const padding = " ".repeat(PADDING - descriptionHalfLength + logoHalfWidth);
84
- if (index === 0) console.log(padding + chalk.bold.hex(PRIMARY_COLOR)(description));
85
- else console.log(padding + description);
86
- });
87
- console.log("");
88
- }
89
- let _printed = false;
90
- const Logo = {
91
- print: printLogo,
92
- printOnce: () => {
93
- if (_printed) return;
94
- _printed = true;
95
- printLogo();
96
- }
97
- };
98
- const SHARED_DATA_PATH = ".rollipop";
99
- //#endregion
100
- //#region src/common/env.ts
101
- const TRUTHY_VALUES = [
102
- "yes",
103
- "on",
104
- "true",
105
- "enabled"
106
- ];
107
- const FALSY_VALUES = [
108
- "no",
109
- "off",
110
- "false",
111
- "disabled"
112
- ];
113
- function parseDebugKeys() {
114
- return Object.keys(process.env).filter((key) => /^debug_/i.test(key)).reduce((acc, key) => {
115
- const prop = key.slice(6).toLowerCase().replace(/_([a-z])/g, (_, key) => key.toUpperCase());
116
- let value = process.env[key];
117
- const lowerCase = typeof value === "string" ? value.toLowerCase() : value.toString();
118
- if (TRUTHY_VALUES.includes(lowerCase)) value = true;
119
- else if (FALSY_VALUES.includes(lowerCase)) value = false;
120
- else value = Boolean(Number(value));
121
- acc[prop] = value;
122
- return acc;
123
- }, {});
124
- }
125
- let debugKeys = null;
126
- function isDebugEnabled() {
127
- if (debugKeys == null) debugKeys = parseDebugKeys();
128
- return debugKeys["rollipop"] ?? false;
129
- }
130
- //#endregion
131
- //#region src/common/logger.ts
132
- var Logger = class Logger {
133
- static blocked = false;
134
- static queuedMessages = [];
135
- static Colors = {
136
- trace: chalk.gray,
137
- debug: chalk.blue,
138
- log: chalk.green,
139
- info: chalk.cyan,
140
- warn: chalk.yellow,
141
- error: chalk.red
142
- };
143
- format = "HH:mm:ss.SSS";
144
- debugEnabled;
145
- static block() {
146
- this.blocked = true;
147
- }
148
- static unblock(flush = true) {
149
- this.blocked = false;
150
- if (flush) for (const args of Logger.queuedMessages) console.log(...args);
151
- Logger.queuedMessages.length = 0;
152
- }
153
- constructor(scope) {
154
- this.scope = scope;
155
- this.debugEnabled = isDebugEnabled();
156
- }
157
- getFormat() {
158
- return this.format;
159
- }
160
- setFormat(format) {
161
- this.format = format;
162
- }
163
- getTimestamp() {
164
- return dayjs().format(this.getFormat());
165
- }
166
- print(logLevel, ...args) {
167
- const timestamp = chalk.gray(this.getTimestamp());
168
- const level = Logger.Colors[logLevel](logLevel);
169
- if (this.scope) args = [
170
- timestamp,
171
- level,
172
- chalk.magenta(this.scope),
173
- ...args
174
- ];
175
- else args = [
176
- timestamp,
177
- level,
178
- ...args
179
- ];
180
- if (Logger.blocked) Logger.queuedMessages.push(args);
181
- else console.log(...args);
182
- }
183
- trace(...args) {
184
- this.debugEnabled && this.print("trace", ...args);
185
- }
186
- debug(...args) {
187
- this.debugEnabled && this.print("debug", ...args);
188
- }
189
- log(...args) {
190
- this.print("log", ...args);
191
- }
192
- info(...args) {
193
- this.print("info", ...args);
194
- }
195
- warn(...args) {
196
- this.print("warn", ...args);
197
- }
198
- error(...args) {
199
- this.print("error", ...args);
200
- }
201
- child(scope) {
202
- invariant(this.scope, "Logger must have a scope to create a child logger");
203
- return new Logger(`${this.scope}:${scope}`);
204
- }
205
- };
206
- //#endregion
207
- //#region src/node/logger.ts
208
- const logger$2 = new Logger("cli");
209
- //#endregion
210
- //#region src/node/utils.ts
211
- function parseBoolean(value) {
212
- return value === "true" || value === "1";
213
- }
214
- function resolvePath(value) {
215
- return path.resolve(value);
216
- }
217
- //#endregion
218
- //#region src/core/fs/data.ts
219
- function getSharedDataPath(basePath) {
220
- return path.join(basePath, SHARED_DATA_PATH);
221
- }
222
- //#endregion
223
- //#region src/core/settings.ts
224
- function getSettingsPath(basePath) {
225
- return path.join(getSharedDataPath(basePath), "settings.json");
226
- }
227
- function loadSettings(basePath) {
228
- const settingsPath = getSettingsPath(basePath);
229
- if (!fs.existsSync(settingsPath)) return {};
230
- return JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
231
- }
232
- function saveSettings(basePath, settings) {
233
- const newSettings = merge(loadSettings(basePath), settings);
234
- fs.writeFileSync(getSettingsPath(basePath), JSON.stringify(newSettings, null, 2));
235
- }
236
- //#endregion
237
- //#region src/node/commands/start/debugger.ts
238
- var DebuggerOpener = class DebuggerOpener {
239
- static MAX_TARGETS_SHOWN = 10;
240
- _prompting = false;
241
- settings;
242
- autoOpened = false;
243
- static setAutoOpenEnabled(projectRoot, enabled) {
244
- saveSettings(projectRoot, { devtools: { autoOpen: enabled } });
245
- }
246
- constructor(projectRoot, serverUrl) {
247
- this.projectRoot = projectRoot;
248
- this.serverUrl = serverUrl;
249
- this.settings = loadSettings(projectRoot);
250
- }
251
- async openDebuggerForTarget(target) {
252
- logger$2.debug(`Opening debugger for target: ${target.id}`);
253
- try {
254
- await fetch(new URL("/open-debugger?target=" + encodeURIComponent(target.id), this.serverUrl), { method: "POST" });
255
- } catch (error) {
256
- logger$2.error(`Failed to open debugger for ${target.title}`);
257
- logger$2.debug("Reason", error);
258
- }
259
- }
260
- async autoOpen() {
261
- if (this.autoOpened) return;
262
- this.autoOpened = true;
263
- if (this.isAutoOpenEnabled()) await this.open();
264
- }
265
- async open() {
266
- logger$2.debug("Fetching available debugging targets...");
267
- const response = await fetch(new URL("/json/list", this.serverUrl), { method: "POST" });
268
- if (response.status !== 200) throw new Error(`Unexpected status code: ${response.status}`);
269
- const targets = await response.json();
270
- if (!Array.isArray(targets)) throw new Error("Unexpected response format");
271
- logger$2.debug(`Found ${targets.length} debugging targets:`);
272
- if (targets.length === 0) logger$2.warn("No connected targets");
273
- else if (targets.length === 1) {
274
- const target = targets[0];
275
- await this.openDebuggerForTarget(target);
276
- } else {
277
- if (targets.length > DebuggerOpener.MAX_TARGETS_SHOWN) logger$2.warn(`More than ${DebuggerOpener.MAX_TARGETS_SHOWN} debug targets available, showing the first ${DebuggerOpener.MAX_TARGETS_SHOWN}.`);
278
- const slicedTargets = targets.slice(0, DebuggerOpener.MAX_TARGETS_SHOWN);
279
- Logger.block();
280
- this._prompting = true;
281
- try {
282
- const targetIndex = await select({
283
- message: "Multiple debug targets available, please select:",
284
- choices: slicedTargets.map((target, index) => ({
285
- value: index,
286
- name: `${target.title} (${target.description})`
287
- }))
288
- });
289
- await this.openDebuggerForTarget(slicedTargets[targetIndex]);
290
- } catch {} finally {
291
- Logger.unblock();
292
- this._prompting = false;
293
- }
294
- }
295
- }
296
- isPrompting() {
297
- return this._prompting;
298
- }
299
- isAutoOpenEnabled() {
300
- return this.settings.devtools?.autoOpen ?? false;
301
- }
302
- setAutoOpenEnabled(enabled) {
303
- const newSettings = this.settings = merge(this.settings, { devtools: { autoOpen: enabled } });
304
- saveSettings(this.projectRoot, newSettings);
305
- }
306
- };
307
- //#endregion
308
- //#region src/node/commands/start/setup-interactive-mode.ts
309
- const CTRL_C = "";
310
- const CTRL_D = "";
311
- const BROADCAST_THROTTLE_DELAY = 500;
312
- function setupInteractiveMode(options) {
313
- const { devServer, extraCommands = [] } = options;
314
- if (!devServer.instance.server.listening) throw new Error("Dev server is not listening. Please call `devServer.instance.listen()` first.");
315
- if (!(process.stdin.isTTY && process.stdin instanceof ReadStream)) {
316
- logger$2.warn("Interactive mode is not supported in non-interactive environments");
317
- return;
318
- }
319
- const debuggerOpener = new DebuggerOpener(devServer.config.root, devServer.instance.listeningOrigin);
320
- const defaultCommands = getDefaultCommands(devServer, debuggerOpener);
321
- const allCommands = [...defaultCommands, ...extraCommands];
322
- assertHasNoDuplicateCommands(defaultCommands, extraCommands);
323
- readline.emitKeypressEvents(process.stdin);
324
- process.stdin.setRawMode(true);
325
- devServer.on("device.connected", () => {
326
- debuggerOpener.autoOpen().catch(() => {
327
- logger$2.error("Failed to open debugger");
328
- });
329
- });
330
- process.stdin.on("keypress", (_, key) => {
331
- const { ctrl = false, shift = false } = key;
332
- const sequence = key.sequence?.toLowerCase();
333
- if (sequence == null || debuggerOpener.isPrompting()) return;
334
- if (ctrl && [CTRL_C, CTRL_D].includes(sequence)) {
335
- process.stdin.setRawMode(false);
336
- process.stdin.pause();
337
- process.emit("SIGINT");
338
- process.exit(0);
339
- }
340
- const targetCommand = allCommands.find((command) => command.key === sequence && (command.shift ?? false) === shift);
341
- if (targetCommand) targetCommand.handler.call({
342
- server: devServer,
343
- logger: logger$2
344
- });
345
- });
346
- console.log();
347
- allCommands.forEach((command, index) => {
348
- if (defaultCommands.length === index) console.log();
349
- const leadingLabel = command.shift ? "»" : "» Press";
350
- const shortcut = chalk.bold(shortcutLabel(command.key, command.shift));
351
- console.log(`${leadingLabel} ${shortcut} │ ${typeof command.description === "function" ? command.description() : command.description}`);
352
- });
353
- }
354
- function getDefaultCommands(devServer, debuggerOpener) {
355
- return [
356
- {
357
- key: "r",
358
- description: "Reload app",
359
- handler: throttle(() => {
360
- logger$2.info("Reloading app...");
361
- devServer.message.broadcast("reload");
362
- }, BROADCAST_THROTTLE_DELAY)
363
- },
364
- {
365
- key: "j",
366
- description: "Open DevTools",
367
- handler: () => {
368
- debuggerOpener.open().catch(() => {
369
- logger$2.error("Failed to open debugger");
370
- });
371
- }
372
- },
373
- {
374
- key: "d",
375
- description: "Show developer menu",
376
- handler: throttle(() => {
377
- logger$2.info("Showing developer menu...");
378
- devServer.message.broadcast("devMenu");
379
- }, BROADCAST_THROTTLE_DELAY)
380
- },
381
- {
382
- key: "d",
383
- shift: true,
384
- description: () => {
385
- const autoOpenEnabled = debuggerOpener.isAutoOpenEnabled();
386
- return `Toggle auto opening developer tools on startup (${chalk.bold(autoOpenEnabled ? "enabled" : "disabled")})`;
387
- },
388
- handler: () => {
389
- const newAutoOpenEnabled = !debuggerOpener.isAutoOpenEnabled();
390
- debuggerOpener.setAutoOpenEnabled(newAutoOpenEnabled);
391
- logger$2.info(`Auto opening developer tools: ${chalk.bold(newAutoOpenEnabled ? "enabled" : "disabled")}`);
392
- }
393
- }
394
- ];
395
- }
396
- function shortcutLabel(key, shift) {
397
- if (shift) return `shift+${key}`;
398
- return key;
399
- }
400
- function assertHasNoDuplicateCommands(defaultCommands, commands) {
401
- const defaultCommandKeys = defaultCommands.map(({ key, shift }) => shortcutLabel(key, shift));
402
- const duplicateKeys = commands.map(({ key, shift }) => shortcutLabel(key, shift)).filter((key) => defaultCommandKeys.includes(key));
403
- const invalidCommandKeys = commands.filter(({ key }) => key.length > 1).map(({ key, shift }) => shortcutLabel(key, shift));
404
- if (invalidCommandKeys.length > 0) throw new Error(`Invalid commands: ${invalidCommandKeys.join(", ")}`);
405
- if (duplicateKeys.length > 0) throw new Error(`Duplicate commands: ${duplicateKeys.join(", ")}`);
406
- }
407
- //#endregion
408
- //#region src/node/cli-utils.ts
409
- function createReactNativeCliCommand(commandDefinition) {
410
- return {
411
- name: commandDefinition.name,
412
- description: commandDefinition.description,
413
- options: commandDefinition.options?.map((option) => omit(option, ["required"])),
414
- func: (_argv, cliConfig, args) => {
415
- Logo.printOnce();
416
- return commandDefinition.action.call({ platforms: Object.keys(cliConfig.platforms) }, args);
417
- }
418
- };
419
- }
420
- //#endregion
421
- //#region src/core/plugins/context.ts
422
- const pluginLogger = new Logger();
423
- function createPluginContext(name) {
424
- return {
425
- debug: (log) => {
426
- printPluginLog("debug", log, name);
427
- },
428
- info: (log) => {
429
- printPluginLog("info", log, name);
430
- },
431
- warn: (log) => {
432
- printPluginLog("warn", log, name);
433
- }
434
- };
435
- }
436
- function printPluginLog(level, log, pluginName = "unknown") {
437
- const pluginLabel = chalk.magenta(`plugin:${pluginName}`);
438
- if (typeof log === "string") pluginLogger[level](pluginLabel, log);
439
- else pluginLogger[level](pluginLabel, log.stack ?? log.message);
440
- }
441
- //#endregion
442
- //#region src/server/logger.ts
443
- const logger$1 = new Logger("dev-server");
444
- var DevServerLogger = class {
445
- level = "trace";
446
- trace(...args) {
447
- logger$1.trace(...args);
448
- }
449
- debug(...args) {
450
- logger$1.debug(...args);
451
- }
452
- info(...args) {
453
- logger$1.info(...args);
454
- }
455
- warn(...args) {
456
- logger$1.warn(...args);
457
- }
458
- error(...args) {
459
- logger$1.error(...args);
460
- }
461
- silent(...args) {
462
- logger$1.trace(chalk.gray("(silent)"), ...args);
463
- }
464
- fatal(...args) {
465
- logger$1.error(chalk.magenta("(fatal)"), ...args);
466
- }
467
- child(_bindings) {
468
- return this;
469
- }
470
- };
471
- //#endregion
472
- //#region src/utils/dev-server.ts
473
- async function assertDevServerStatus(options) {
474
- const { devServerUrl, projectRoot, port } = options;
475
- const status = await getDevServerStatus(devServerUrl, projectRoot);
476
- let shouldExit = false;
477
- switch (status) {
478
- case DevServerStatus.MATCHED_SERVER_RUNNING:
479
- logger$1.warn(`A dev server is already running for this project on port ${port}. Exiting.`);
480
- shouldExit = true;
481
- break;
482
- case DevServerStatus.PORT_TAKEN:
483
- logger$1.error(`Another process is running on port ${port}. Please terminate this process and try again, or use another port with "--port".`);
484
- shouldExit = true;
485
- break;
486
- }
487
- if (shouldExit) process.exit(1);
488
- }
489
- var DevServerStatus = /* @__PURE__ */ function(DevServerStatus) {
490
- DevServerStatus[DevServerStatus["NOT_RUNNING"] = 0] = "NOT_RUNNING";
491
- DevServerStatus[DevServerStatus["MATCHED_SERVER_RUNNING"] = 1] = "MATCHED_SERVER_RUNNING";
492
- DevServerStatus[DevServerStatus["PORT_TAKEN"] = 2] = "PORT_TAKEN";
493
- DevServerStatus[DevServerStatus["UNKNOWN"] = 3] = "UNKNOWN";
494
- return DevServerStatus;
495
- }(DevServerStatus || {});
496
- async function getDevServerStatus(devServerUrl, projectRoot) {
497
- const { hostname, port } = new URL(devServerUrl);
498
- try {
499
- if (!await isPortOccupied(hostname, port)) return DevServerStatus.NOT_RUNNING;
500
- const statusResponse = await fetch(`${devServerUrl}/status`);
501
- return await statusResponse.text() === "packager-status:running" && statusResponse.headers.get("X-React-Native-Project-Root") === projectRoot ? DevServerStatus.MATCHED_SERVER_RUNNING : DevServerStatus.PORT_TAKEN;
502
- } catch {
503
- return DevServerStatus.UNKNOWN;
504
- }
505
- }
506
- async function isPortOccupied(host, port) {
507
- let result = false;
508
- const server = http.createServer();
509
- return new Promise((resolve, reject) => {
510
- server.once("error", (error) => {
511
- server.close();
512
- if (error.code === "EADDRINUSE") result = true;
513
- else reject(error);
514
- });
515
- server.once("listening", () => {
516
- result = false;
517
- server.close();
518
- });
519
- server.once("close", () => resolve(result));
520
- server.listen({
521
- host,
522
- port
523
- });
524
- });
525
- }
526
- //#endregion
527
- //#region src/utils/build-options.ts
528
- const DEFAULT_BUILD_OPTIONS = {
529
- cache: true,
530
- minify: false
531
- };
532
- function resolveBuildOptions(config, buildOptions) {
533
- if (buildOptions.outfile) buildOptions.outfile = path.resolve(config.root, buildOptions.outfile);
534
- if ((buildOptions.sourcemap === true || buildOptions.sourcemap === "hidden") && buildOptions.sourcemapOutfile) buildOptions.sourcemapOutfile = path.resolve(config.root, buildOptions.sourcemapOutfile);
535
- return merge(DEFAULT_BUILD_OPTIONS, {
536
- ...buildOptions,
537
- dev: buildOptions.dev ?? config.mode === "development"
538
- });
539
- }
540
- //#endregion
541
- //#region src/constants.ts
542
- const ROLLIPOP_VERSION = globalThis.__ROLLIPOP_VERSION__;
543
- const GLOBAL_IDENTIFIER = "global";
544
- /**
545
- * @see {@link https://github.com/facebook/metro/blob/0.81.x/docs/Configuration.md#resolvermainfields}
546
- */
547
- const DEFAULT_RESOLVER_MAIN_FIELDS = [
548
- "react-native",
549
- "browser",
550
- "main"
551
- ];
552
- const DEFAULT_RESOLVER_ALIAS_FIELDS = [["react-native"], ["browser"]];
553
- const DEFAULT_RESOLVER_CONDITION_NAMES = ["react-native"];
554
- /**
555
- * Unlike the Metro bundler configuration, this prioritizes resolving TypeScript and ESM first.
556
- *
557
- * @see {@link https://github.com/facebook/metro/blob/0.81.x/packages/metro-config/src/defaults/defaults.js}
558
- * @see {@link https://github.com/facebook/metro/blob/0.81.x/packages/metro-file-map/src/workerExclusionList.js}
559
- */
560
- const DEFAULT_SOURCE_EXTENSIONS = [
561
- "ts",
562
- "tsx",
563
- "js",
564
- "jsx",
565
- "mjs",
566
- "cjs",
567
- "json"
568
- ];
569
- const DEFAULT_IMAGE_EXTENSIONS = [
570
- "bmp",
571
- "gif",
572
- "jpg",
573
- "jpeg",
574
- "png",
575
- "psd",
576
- "svg",
577
- "webp"
578
- ];
579
- const IMAGE_EXTENSIONS = [
580
- ...DEFAULT_IMAGE_EXTENSIONS,
581
- "tiff",
582
- "ktx"
583
- ];
584
- const DEFAULT_ASSET_EXTENSIONS = [
585
- ...DEFAULT_IMAGE_EXTENSIONS,
586
- "xml",
587
- "m4v",
588
- "mov",
589
- "mp4",
590
- "mpeg",
591
- "mpg",
592
- "webm",
593
- "aac",
594
- "aiff",
595
- "caf",
596
- "m4a",
597
- "mp3",
598
- "wav",
599
- "html",
600
- "pdf",
601
- "yaml",
602
- "yml",
603
- "otf",
604
- "ttf",
605
- "zip"
606
- ];
607
- const DEFAULT_ASSET_REGISTRY_PATH = "react-native/Libraries/Image/AssetRegistry.js";
608
- const DEFAULT_HMR_CLIENT_PATH = "react-native/Libraries/Utilities/HMRClient.js";
609
- const DEFAULT_REACT_NATIVE_GLOBAL_IDENTIFIERS = [
610
- GLOBAL_IDENTIFIER,
611
- "Promise",
612
- "regeneratorRuntime",
613
- "XMLHttpRequest",
614
- "FormData",
615
- "fetch",
616
- "Headers",
617
- "Request",
618
- "Response",
619
- "WebSocket",
620
- "Blob",
621
- "File",
622
- "FileReader",
623
- "URL",
624
- "URLSearchParams",
625
- "AbortController",
626
- "AbortSignal",
627
- "queueMicrotask",
628
- "setImmediate",
629
- "clearImmediate",
630
- "requestIdleCallback",
631
- "cancelIdleCallback",
632
- "setTimeout",
633
- "clearTimeout",
634
- "setInterval",
635
- "clearInterval",
636
- "requestAnimationFrame",
637
- "cancelAnimationFrame",
638
- "DOMRect",
639
- "DOMRectReadOnly",
640
- "DOMRectList",
641
- "HTMLCollection",
642
- "NodeList",
643
- "Node",
644
- "Document",
645
- "CharacterData",
646
- "Text",
647
- "Element",
648
- "HTMLElement",
649
- "IntersectionObserver",
650
- "MutationObserver",
651
- "MutationRecord",
652
- "EventCounts",
653
- "Performance",
654
- "PerformanceEntry",
655
- "PerformanceEventTiming",
656
- "PerformanceLongTaskTiming",
657
- "PerformanceMark",
658
- "PerformanceMeasure",
659
- "PerformanceObserver",
660
- "PerformanceObserverEntryList",
661
- "PerformanceResourceTiming",
662
- "TaskAttributionTiming"
663
- ];
664
- const DEFAULT_ENV_PREFIX = "ROLLIPOP_";
665
- const DEFAULT_ENV_FILE = ".env";
666
- const DEFAULT_RUNTIME_TARGET = "hermes-v1";
667
- //#endregion
668
- //#region src/utils/hash.ts
669
- function md5(data) {
670
- return crypto.createHash("md5").update(data).digest("hex");
671
- }
672
- //#endregion
673
- //#region src/utils/serialize.ts
674
- function serialize(value) {
675
- return JSON.stringify(value, (_, value) => {
676
- if (typeof value === "function") return value.toString();
677
- if (value instanceof RegExp) return value.toString();
678
- return value;
679
- });
680
- }
681
- //#endregion
682
- //#region src/utils/id.ts
683
- function createId(config, buildOptions) {
684
- return md5(serialize([
685
- ROLLIPOP_VERSION,
686
- filterTransformAffectedOptions(buildOptions),
687
- filterTransformAffectedConfig(config)
688
- ]));
689
- }
690
- function filterTransformAffectedOptions(buildOptions) {
691
- return pick(buildOptions, ["platform", "dev"]);
692
- }
693
- function filterTransformAffectedConfig(config) {
694
- const { transformer, serializer, reactNative, devMode, plugins = [] } = config;
695
- return [
696
- transformer,
697
- serializer.polyfills,
698
- serializer.prelude,
699
- reactNative.assetRegistryPath,
700
- devMode,
701
- plugins.map((plugin, index) => `${plugin.name}#${index}`)
702
- ];
703
- }
704
- //#endregion
705
- //#region src/core/fs/storage.ts
706
- const DEFAULT_DATA = { build: {} };
707
- var FileStorage = class FileStorage {
708
- static instance = null;
709
- dataFilePath;
710
- data;
711
- static getInstance(basePath) {
712
- if (FileStorage.instance == null) FileStorage.instance = new FileStorage(basePath);
713
- return FileStorage.instance;
714
- }
715
- constructor(basePath) {
716
- this.basePath = basePath;
717
- this.dataFilePath = path.join(getSharedDataPath(basePath), "rollipop.json");
718
- if (fs.existsSync(this.dataFilePath)) this.data = JSON.parse(fs.readFileSync(this.dataFilePath, "utf-8"));
719
- else this.data = DEFAULT_DATA;
720
- }
721
- get() {
722
- return this.data;
723
- }
724
- set(data) {
725
- this.data = merge(this.data, data);
726
- fs.writeFileSync(this.dataFilePath, JSON.stringify(this.data, null, 2));
727
- }
728
- };
729
- //#endregion
730
- //#region src/utils/string.ts
731
- function indent(text, indent, space = " ") {
732
- return text.replace(/^/gm, space.repeat(indent));
733
- }
734
- //#endregion
735
- //#region src/common/code.ts
736
- function asLiteral(value) {
737
- return JSON.stringify(value);
738
- }
739
- function nodeEnvironment(dev) {
740
- return dev ? "development" : "production";
741
- }
742
- function iife(body, path = "<unknown>") {
743
- const bodyPlaceholder = "__BODY__";
744
- return dedent`
745
- // ${path}
746
- (function (global) {
747
- ${bodyPlaceholder}
748
- })(${GLOBAL_IDENTIFIER});
749
- `.replace(bodyPlaceholder, indent(body, 1));
750
- }
751
- //#endregion
752
- //#region src/config/compose-override.ts
753
- async function applyOverrideRolldownOptions(override, rolldownOptions) {
754
- if (typeof override === "function") return await override(rolldownOptions);
755
- return {
756
- input: merge(rolldownOptions.input ?? {}, override.input ?? {}),
757
- output: merge(rolldownOptions.output ?? {}, override.output ?? {})
758
- };
759
- }
760
- function composeOverrideRolldownOptions(target, source) {
761
- if (source == null) return target;
762
- if (target == null) return source;
763
- return async (rolldownOptions) => {
764
- return await applyOverrideRolldownOptions(source, await applyOverrideRolldownOptions(target, rolldownOptions));
765
- };
766
- }
767
- //#endregion
768
- //#region src/internal/react-native.ts
769
- function getInitializeCorePath(basePath) {
770
- return __require.resolve("react-native/Libraries/Core/InitializeCore", { paths: [basePath] });
771
- }
772
- function getPolyfillScriptPaths(reactNativePath) {
773
- return __require(path.join(reactNativePath, "rn-get-polyfills"))();
774
- }
775
- function getGlobalVariables(dev, buildType) {
776
- const isDevServerMode = dev && buildType === "serve";
777
- return [
778
- `var __BUNDLE_START_TIME__=globalThis.nativePerformanceNow?nativePerformanceNow():Date.now();`,
779
- `var __DEV__=${dev};`,
780
- `var ${GLOBAL_IDENTIFIER}=typeof globalThis!=='undefined'?globalThis:typeof global !== 'undefined'?global:typeof window!=='undefined'?window:this;`,
781
- `var process=globalThis.process||{};process.env=process.env||{};process.env.NODE_ENV=process.env.NODE_ENV||"${dev ? "development" : "production"}";`,
782
- isDevServerMode ? `var $RefreshReg$ = () => {};` : null,
783
- isDevServerMode ? `var $RefreshSig$ = () => (v) => v;` : null
784
- ].filter(isNotNil);
785
- }
786
- //#endregion
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);
809
- onEvent?.(event);
810
- } };
811
- return config;
812
- }
813
- function resolveHmrConfig(config) {
814
- if (config.mode !== "development") return null;
815
- const defaultRuntimeImplements = getDefaultRuntimeImplements();
816
- if (typeof config.devMode.hmr === "boolean") return config.devMode.hmr ? defaultRuntimeImplements : null;
817
- const { runtimeImplement = defaultRuntimeImplements.runtimeImplement, clientImplement = defaultRuntimeImplements.clientImplement } = config.devMode.hmr;
818
- return {
819
- runtimeImplement,
820
- clientImplement
821
- };
822
- }
823
- getDefaultRuntimeImplements.cache = null;
824
- function getDefaultRuntimeImplements() {
825
- if (getDefaultRuntimeImplements.cache == null) getDefaultRuntimeImplements.cache = {
826
- runtimeImplement: fs.readFileSync(__require.resolve("rollipop/hmr-runtime"), "utf-8"),
827
- clientImplement: fs.readFileSync(__require.resolve("rollipop/hmr-client"), "utf-8")
828
- };
829
- return getDefaultRuntimeImplements.cache;
830
- }
831
- //#endregion
832
- //#region src/utils/env.ts
833
- function defineEnvFromObject(env) {
834
- return Object.fromEntries(Object.entries(env).map(([key, value]) => [`import.meta.env.${key}`, asLiteral(value)]));
835
- }
836
- //#endregion
837
- //#region src/utils/node-resolve.ts
838
- function resolveFrom(basePath, lookupPath) {
839
- if (path.isAbsolute(lookupPath)) return lookupPath;
840
- return __require.resolve(lookupPath, { paths: [basePath] });
841
- }
842
- function resolvePackagePath(basePath, packageName) {
843
- let packagePath = null;
844
- try {
845
- packagePath = resolvePackagePathWithNodeRequire(basePath, packageName, "package.json");
846
- if (packagePath) return packagePath;
847
- } catch {}
848
- try {
849
- packagePath = resolvePackagePathWithNodeRequire(basePath, packageName);
850
- if (packagePath) return packagePath;
851
- } catch {}
852
- throw new Error(`Failed to resolve package path for '${packageName}'`);
853
- }
854
- function resolvePackageJson(basePath, packageName) {
855
- try {
856
- const packagePath = resolvePackagePath(basePath, packageName);
857
- const packageJsonPath = path.join(packagePath, "package.json");
858
- const rawPackageJson = fs.readFileSync(packageJsonPath, "utf-8");
859
- return JSON.parse(rawPackageJson);
860
- } catch {
861
- return null;
862
- }
863
- }
864
- function resolvePackagePathWithNodeRequire(basePath, packageName, subpath) {
865
- const resolvedPath = __require.resolve(subpath ? `${packageName}/${subpath}` : packageName, { paths: [basePath] });
866
- const root = path.parse(resolvedPath).root;
867
- let currentPath = path.dirname(resolvedPath);
868
- while (currentPath !== root) {
869
- if (fs.existsSync(path.join(currentPath, "package.json"))) return currentPath;
870
- currentPath = path.dirname(currentPath);
871
- }
872
- return null;
873
- }
874
- //#endregion
875
- //#region src/utils/terminal.ts
876
- /**
877
- * Based on https://github.com/sindresorhus/log-update/blob/master/index.js
878
- * Based on https://github.com/unjs/webpackbar
879
- */
880
- function eraseLines(count) {
881
- let clear = "";
882
- for (let i = 0; i < count; i++) clear += `\u001B[2K` + (i < count - 1 ? `\u001B[1A` : "");
883
- if (count) clear += `\u001B[G`;
884
- return clear;
885
- }
886
- function ellipsisLeft(value, maxLength) {
887
- if (value.length <= maxLength - 3) return value;
888
- return `...${value.slice(value.length - maxLength - 1)}`;
889
- }
890
- const originalWrite = Symbol("original-write");
891
- var StreamManager = class {
892
- prevLineCount;
893
- listening;
894
- extraLines;
895
- _streams;
896
- constructor() {
897
- this.prevLineCount = 0;
898
- this.listening = false;
899
- this.extraLines = "";
900
- this._onData = this._onData.bind(this);
901
- this._streams = [process.stdout, process.stderr];
902
- }
903
- render(lines) {
904
- this.listen();
905
- const wrappedLines = wrapAnsi(lines, this.columns, {
906
- trim: false,
907
- hard: true,
908
- wordWrap: false
909
- });
910
- const data = eraseLines(this.prevLineCount) + wrappedLines + "\n" + this.extraLines;
911
- this.write(data);
912
- this.prevLineCount = data.split("\n").length;
913
- }
914
- get columns() {
915
- return (process.stderr.columns || 80) - 2;
916
- }
917
- write(data) {
918
- const stream = process.stderr;
919
- if (stream.write[originalWrite]) stream.write[originalWrite].call(stream, data, "utf8");
920
- else stream.write(data, "utf8");
921
- }
922
- clear() {
923
- this.done();
924
- this.write(eraseLines(this.prevLineCount));
925
- }
926
- done() {
927
- this.stopListen();
928
- this.prevLineCount = 0;
929
- this.extraLines = "";
930
- }
931
- _onData(data) {
932
- const lines = String(data).split("\n").length - 1;
933
- if (lines > 0) {
934
- this.prevLineCount += lines;
935
- this.extraLines += data;
936
- }
937
- }
938
- listen() {
939
- if (this.listening) return;
940
- for (const stream of this._streams) {
941
- if (stream.write[originalWrite]) continue;
942
- const write = (data, ...args) => {
943
- if (!stream.write[originalWrite]) return stream.write(data, ...args);
944
- this._onData(data);
945
- return stream.write[originalWrite].call(stream, data, ...args);
946
- };
947
- write[originalWrite] = stream.write;
948
- stream.write = write;
949
- }
950
- this.listening = true;
951
- }
952
- stopListen() {
953
- for (const stream of this._streams) if (stream.write[originalWrite]) stream.write = stream.write[originalWrite];
954
- this.listening = false;
955
- }
956
- };
957
- //#endregion
958
- //#region src/common/progress-bar.ts
959
- const BAR_LENGTH = 25;
960
- const BLOCK_CHAR = "█";
961
- const idleRenderer = { render(_state, context) {
962
- return ` ${chalk.gray("Waiting...")} ${chalk.gray(context.label)}\n`;
963
- } };
964
- const runningRenderer = { render(state, context) {
965
- const { label, current, total, columns } = context;
966
- const unknownTotal = total === 0;
967
- const progress = unknownTotal ? 0 : current / total * 100;
968
- const width = unknownTotal ? 0 : progress * (BAR_LENGTH / 100);
969
- const bg = chalk.white(BLOCK_CHAR);
970
- const fg = chalk.cyan(BLOCK_CHAR);
971
- const bar = range(BAR_LENGTH).map((n) => n < width ? fg : bg).join("");
972
- const progressLabel = unknownTotal ? chalk.gray("(calculating...)") : `(${progress.toFixed(2)}%)`;
973
- const moduleCountLabel = unknownTotal ? `${current} modules` : `${current}/${total} modules`;
974
- return `${[
975
- chalk.cyan("●"),
976
- bar,
977
- progressLabel,
978
- chalk.gray(moduleCountLabel),
979
- chalk.gray(label)
980
- ].join(" ")}\n${state.moduleId ? " " + chalk.grey(ellipsisLeft(state.moduleId, columns - 10)) : ""}`;
981
- } };
982
- const completedRenderer = { render(state, context) {
983
- if (state.hasErrors) return `${chalk.red("✘")} Build failed ${chalk.gray(context.label)}`;
984
- else {
985
- const icon = chalk.green("✔");
986
- const durationInSeconds = (state.duration / 1e3).toFixed(2);
987
- return `${`${icon} Build completed ${chalk.gray(context.label)}`}\n${chalk.grey(` Built in ${durationInSeconds}s (${context.current}/${context.total} modules)`)}`;
988
- }
989
- } };
990
- var ProgressBarRenderer = class {
991
- renderers = {
992
- idle: idleRenderer,
993
- running: runningRenderer,
994
- completed: completedRenderer
995
- };
996
- render(state, context) {
997
- return this.renderers[state.type].render(state, context);
998
- }
999
- };
1000
- var ProgressBar = class {
1001
- columns = (process.stderr.columns || 80) - 2;
1002
- renderer;
1003
- label;
1004
- state = { type: "idle" };
1005
- current = 0;
1006
- total;
1007
- stale = false;
1008
- constructor(options) {
1009
- this.total = options.total;
1010
- this.label = options.label;
1011
- this.renderer = options.renderer ?? new ProgressBarRenderer();
1012
- }
1013
- get done() {
1014
- return this.state.type === "completed";
1015
- }
1016
- setCurrent(current) {
1017
- this.current = current;
1018
- this.stale = true;
1019
- return this;
1020
- }
1021
- setTotal(total) {
1022
- this.total = total;
1023
- this.stale = true;
1024
- return this;
1025
- }
1026
- start() {
1027
- this.state = { type: "running" };
1028
- this.stale = true;
1029
- return this;
1030
- }
1031
- setModuleId(moduleId) {
1032
- if (this.state.type !== "running") return this;
1033
- this.state = {
1034
- type: "running",
1035
- moduleId
1036
- };
1037
- this.stale = true;
1038
- return this;
1039
- }
1040
- complete(duration, hasErrors = false) {
1041
- this.state = {
1042
- type: "completed",
1043
- duration,
1044
- hasErrors
1045
- };
1046
- this.stale = true;
1047
- return this;
1048
- }
1049
- render() {
1050
- this.stale = false;
1051
- const context = {
1052
- label: this.label,
1053
- current: this.current,
1054
- total: this.total,
1055
- columns: this.columns
1056
- };
1057
- return this.renderer.render(this.state, context);
1058
- }
1059
- };
1060
- var ProgressBarRenderManager = class ProgressBarRenderManager {
1061
- static instance = null;
1062
- streamManager = new StreamManager();
1063
- progressBars = /* @__PURE__ */ new Map();
1064
- throttledRender;
1065
- static getInstance() {
1066
- if (!ProgressBarRenderManager.instance) ProgressBarRenderManager.instance = new ProgressBarRenderManager();
1067
- return ProgressBarRenderManager.instance;
1068
- }
1069
- constructor() {
1070
- this.throttledRender = throttle(this._render.bind(this), 50);
1071
- }
1072
- _render() {
1073
- const renderedLines = Array.from(this.progressBars.values().filter((progressBar) => progressBar.stale).map((progressBar) => progressBar.render()));
1074
- if (renderedLines.length > 0) this.streamManager.render(renderedLines.join("\n\n"));
1075
- }
1076
- register(key, options) {
1077
- const progressBar = this.progressBars.get(key);
1078
- if (progressBar == null) {
1079
- const newProgressBar = new ProgressBar(options);
1080
- this.progressBars.set(key, newProgressBar);
1081
- return newProgressBar;
1082
- }
1083
- return progressBar;
1084
- }
1085
- start() {
1086
- console.log();
1087
- this.streamManager.listen();
1088
- this._render();
1089
- }
1090
- render() {
1091
- this.throttledRender();
1092
- }
1093
- release() {
1094
- if (this.progressBars.values().every((progressBar) => progressBar.done)) {
1095
- this._render();
1096
- this.streamManager.done();
1097
- console.log();
1098
- }
1099
- }
1100
- clear() {
1101
- this.streamManager.clear();
1102
- }
1103
- };
1104
- //#endregion
1105
- //#region src/logger.ts
1106
- const logger = new Logger("bundler");
1107
- //#endregion
1108
- //#region src/utils/reporters.ts
1109
- function mergeReporters(reporters) {
1110
- return { update(event) {
1111
- reporters.forEach((reporter) => reporter.update(event));
1112
- } };
1113
- }
1114
- var ClientLogReporter = class {
1115
- logger = new Logger("app");
1116
- update(event) {
1117
- if (event.type === "client_log") {
1118
- if (event.level === "group" || event.level === "groupCollapsed") {
1119
- this.logger.info(...event.data);
1120
- return;
1121
- } else if (event.level === "groupEnd") return;
1122
- this.logger[event.level](...event.data);
1123
- }
1124
- }
1125
- };
1126
- var ProgressFlags = /* @__PURE__ */ function(ProgressFlags) {
1127
- ProgressFlags[ProgressFlags["NONE"] = 0] = "NONE";
1128
- ProgressFlags[ProgressFlags["BUILD_IN_PROGRESS"] = 1] = "BUILD_IN_PROGRESS";
1129
- ProgressFlags[ProgressFlags["FILE_CHANGED"] = 2] = "FILE_CHANGED";
1130
- return ProgressFlags;
1131
- }(ProgressFlags || {});
1132
- var ProgressBarStatusReporter = class {
1133
- renderManager = ProgressBarRenderManager.getInstance();
1134
- progressBar;
1135
- flags = ProgressFlags.NONE;
1136
- constructor(id, label, initialTotalModules) {
1137
- this.progressBar = this.renderManager.register(id, {
1138
- label,
1139
- total: initialTotalModules
1140
- });
1141
- }
1142
- renderProgress(id, totalModules, transformedModules) {
1143
- if (totalModules != null) this.progressBar.setTotal(totalModules);
1144
- this.progressBar.setCurrent(transformedModules).setModuleId(id);
1145
- this.renderManager.render();
1146
- }
1147
- update(event) {
1148
- switch (event.type) {
1149
- case "bundle_build_started":
1150
- this.flags |= ProgressFlags.BUILD_IN_PROGRESS;
1151
- this.progressBar.start();
1152
- this.renderManager.start();
1153
- break;
1154
- case "bundle_build_failed":
1155
- this.flags = ProgressFlags.NONE;
1156
- this.progressBar.complete(0, true);
1157
- this.renderManager.release();
1158
- break;
1159
- case "bundle_build_done":
1160
- this.flags = ProgressFlags.NONE;
1161
- this.progressBar.setTotal(event.totalModules).complete(event.duration, false);
1162
- this.renderManager.release();
1163
- break;
1164
- case "transform":
1165
- const { id, totalModules, transformedModules } = event;
1166
- if (this.flags & ProgressFlags.FILE_CHANGED) {
1167
- logger.debug("Transformed changed file", { id });
1168
- return;
1169
- }
1170
- this.renderProgress(id, totalModules, transformedModules);
1171
- break;
1172
- case "watch_change":
1173
- this.flags |= ProgressFlags.FILE_CHANGED;
1174
- break;
1175
- }
1176
- }
1177
- };
1178
- var CompatStatusReporter = class {
1179
- update(event) {
1180
- switch (event.type) {
1181
- case "bundle_build_started":
1182
- logger.info("Build started...");
1183
- break;
1184
- case "bundle_build_failed":
1185
- logger.error(`Build failed`);
1186
- break;
1187
- case "bundle_build_done":
1188
- const { duration, totalModules } = event;
1189
- const time = chalk.blue(`${duration.toFixed(2)}ms`);
1190
- const modules = chalk.blue(`(${totalModules} modules)`);
1191
- logger.info(`Build completed in ${time} ${modules}`);
1192
- break;
1193
- }
1194
- }
1195
- };
1196
- //#endregion
1197
- //#region src/utils/runtime-target.ts
1198
- function resolveRuntimeTarget(target) {
1199
- switch (target) {
1200
- case "hermes": return "Hermes";
1201
- default: return "HermesV1";
1202
- }
1203
- }
1204
- //#endregion
1205
- //#region src/utils/server.ts
1206
- function getBaseUrl(host, port, https) {
1207
- return `${https ? "https" : "http"}://${host}:${port}`;
1208
- }
1209
- //#endregion
1210
- //#region src/utils/storage.ts
1211
- function getBuildTotalModules(storage, id) {
1212
- return storage.get().build[id]?.totalModules ?? 0;
1213
- }
1214
- //#endregion
1215
- //#region src/core/env.ts
1216
- function loadEnv(options) {
1217
- const { envDir, envPrefix, envFile, mode } = options;
1218
- invariant(envPrefix.length > 0, "`envPrefix` is required");
1219
- invariant(envFile.length > 0, "`envFile` is required");
1220
- const env = {};
1221
- const envFilesToLoad = [
1222
- envFile,
1223
- `${envFile}.local`,
1224
- mode ? `${envFile}.${mode}` : null,
1225
- mode ? `${envFile}.${mode}.local` : null
1226
- ].filter(isNotNil);
1227
- for (const file of envFilesToLoad) {
1228
- const envPath = path.resolve(envDir, file);
1229
- if (!fs.existsSync(envPath)) continue;
1230
- logger.trace(`Loading environment variables from ${envPath}`);
1231
- const parsed = dotenv.parse(fs.readFileSync(envPath, "utf-8"));
1232
- const expanded = dotenvExpand.expand({
1233
- parsed,
1234
- processEnv: {}
1235
- });
1236
- if (expanded.parsed) Object.entries(expanded.parsed).forEach(([key, value]) => {
1237
- if (key.startsWith(envPrefix)) env[key] = key in process.env ? process.env[key] : value;
1238
- });
1239
- }
1240
- logger.trace("Loaded environment variables:", env);
1241
- return env;
1242
- }
1243
- //#endregion
1244
- //#region src/common/transformer.ts
1245
- async function stripFlowTypes(id, code) {
1246
- return await transform({
1247
- filename: id,
1248
- source: code,
1249
- sourcemap: true,
1250
- dialect: "flow",
1251
- format: "pretty"
1252
- });
1253
- }
1254
- //#endregion
1255
- //#region src/core/assets.ts
1256
- /**
1257
- * **NOTE**: Type definitions are ported from `metro` implementation.
1258
- *
1259
- * @see https://github.com/facebook/metro/blob/0.81.x/packages/metro/src/Assets.js
1260
- */
1261
- const SCALE_PATTERN = "@(\\d+\\.?\\d*)x";
1262
- const IMAGE_ASSET_TYPES = new Set(IMAGE_EXTENSIONS);
1263
- /**
1264
- * key: platform,
1265
- * value: allowed scales
1266
- *
1267
- * @see https://github.com/facebook/react-native/blob/0.83-stable/packages/community-cli-plugin/src/commands/bundle/filterPlatformAssetScales.js#L11
1268
- */
1269
- const ALLOW_SCALES = { ios: [
1270
- 1,
1271
- 2,
1272
- 3
1273
- ] };
1274
- /**
1275
- * @see https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp
1276
- */
1277
- const ANDROID_ASSET_QUALIFIER = {
1278
- .75: "ldpi",
1279
- 1: "mdpi",
1280
- 1.5: "hdpi",
1281
- 2: "xhdpi",
1282
- 3: "xxhdpi",
1283
- 4: "xxxhdpi"
1284
- };
1285
- async function resolveScaledAssets(options) {
1286
- const { projectRoot, assetPath, platform, preferNativePlatform } = options;
1287
- const context = {
1288
- platform,
1289
- preferNativePlatform
1290
- };
1291
- const extension = path.extname(assetPath);
1292
- const type = extension.substring(1);
1293
- const relativePath = path.relative(projectRoot, assetPath);
1294
- const dirname = path.dirname(assetPath);
1295
- const files = fs.readdirSync(dirname);
1296
- const stripedBasename = stripSuffix(assetPath, context);
1297
- const suffixPattern = platformSuffixPattern(context);
1298
- const assetRegExp = new RegExp(`${stripedBasename}(${SCALE_PATTERN})?(?:${suffixPattern})?${extension}$`);
1299
- const scaledAssets = {};
1300
- for (const file of files.sort((a, b) => getAssetPriority(b, context) - getAssetPriority(a, context))) {
1301
- const match = assetRegExp.exec(file);
1302
- if (match) {
1303
- const [, , scale = "1"] = match;
1304
- if (scaledAssets[scale]) continue;
1305
- scaledAssets[scale] = file;
1306
- }
1307
- }
1308
- if (!(Object.keys(scaledAssets).length && scaledAssets[1])) throw new Error(`cannot resolve base asset of ${assetPath}`);
1309
- const imageData = fs.readFileSync(assetPath);
1310
- const dimensions = IMAGE_ASSET_TYPES.has(type) ? imageSize(imageData) : void 0;
1311
- const filteredScaledAssets = Object.entries(scaledAssets).map(([scale, file]) => ({
1312
- scale: parseFloat(scale),
1313
- file
1314
- })).filter(({ scale }) => ALLOW_SCALES[platform]?.includes(scale) ?? true).reduce((acc, { scale, file }) => {
1315
- acc.files.push(file);
1316
- acc.scales.push(scale);
1317
- return acc;
1318
- }, {
1319
- scales: [],
1320
- files: []
1321
- });
1322
- return {
1323
- __packager_asset: true,
1324
- id: assetPath,
1325
- name: stripedBasename.replace(extension, ""),
1326
- type,
1327
- width: dimensions?.width,
1328
- height: dimensions?.height,
1329
- files: filteredScaledAssets.files,
1330
- scales: filteredScaledAssets.scales,
1331
- fileSystemLocation: path.dirname(assetPath),
1332
- httpServerLocation: path.join(DEV_SERVER_ASSET_PATH, path.dirname(relativePath)),
1333
- hash: md5(imageData)
1334
- };
1335
- }
1336
- function platformSuffixPattern(context) {
1337
- return [context.platform, context.preferNativePlatform ? "native" : null].filter(isNotNil).map((platform) => `.${platform}`).join("|");
1338
- }
1339
- function stripSuffix(assetPath, context) {
1340
- const basename = path.basename(assetPath);
1341
- const extension = path.extname(assetPath);
1342
- const suffixPattern = platformSuffixPattern(context);
1343
- return basename.replace(new RegExp(`(${SCALE_PATTERN})?(?:${suffixPattern})?${extension}$`), "");
1344
- }
1345
- function getAssetPriority(assetPath, context) {
1346
- const suffixPattern = platformSuffixPattern(context);
1347
- if (new RegExp(`${SCALE_PATTERN}(?:${suffixPattern})`).test(assetPath)) return 3;
1348
- else if (new RegExp(`(?:${suffixPattern})`).test(assetPath)) return 2;
1349
- else if (new RegExp(`${SCALE_PATTERN}`).test(assetPath)) return 1;
1350
- return 0;
1351
- }
1352
- function addSuffix(assetPath, context, options) {
1353
- const extension = path.extname(assetPath);
1354
- return stripSuffix(assetPath, context).concat(options?.scale ? `@${options.scale}x` : "").concat(options?.platform ? `.${options.platform}${extension}` : extension);
1355
- }
1356
- /**
1357
- * add suffix to asset path
1358
- *
1359
- * ```js
1360
- * // assetPath input
1361
- * '/path/to/assets/image.png'
1362
- *
1363
- * // `platform` suffixed
1364
- * '/path/to/assets/image.android.png'
1365
- *
1366
- * // `scale` suffixed
1367
- * '/path/to/assets/image@1x.png'
1368
- *
1369
- * // both `platform` and `scale` suffixed
1370
- * '/path/to/assets/image@1x.android.png'
1371
- * ```
1372
- */
1373
- function getSuffixedPath(assetPath, context, options) {
1374
- const suffixedBasename = addSuffix(assetPath, context, {
1375
- scale: options?.scale,
1376
- platform: options?.platform
1377
- });
1378
- const dirname = path.dirname(assetPath);
1379
- return path.join(dirname, suffixedBasename);
1380
- }
1381
- function resolveAssetPath(assetPath, context, scale) {
1382
- const suffixedPaths = [
1383
- getSuffixedPath(assetPath, context, {
1384
- scale,
1385
- platform: context.platform
1386
- }),
1387
- context.preferNativePlatform ? getSuffixedPath(assetPath, context, {
1388
- scale,
1389
- platform: "native"
1390
- }) : null,
1391
- getSuffixedPath(assetPath, context, { scale })
1392
- ].filter(isNotNil);
1393
- /**
1394
- * When scale is 1, filename can be suffixed or non-suffixed(`image.png`).
1395
- *
1396
- * - Suffixed
1397
- * - `filename.<platform>@<scale>x.ext`
1398
- * - `filename.<platform>.ext`
1399
- * - `filename@<scale>x.ext`
1400
- * - Non suffixed
1401
- * - `filename.ext`
1402
- *
1403
- * 1. Resolve non-suffixed asset first.
1404
- * 2. If file is not exist, resolve suffixed path.
1405
- */
1406
- if (scale === 1) try {
1407
- fs.statSync(assetPath);
1408
- return assetPath;
1409
- } catch {}
1410
- for (const suffixedPath of suffixedPaths) try {
1411
- fs.statSync(suffixedPath);
1412
- return suffixedPath;
1413
- } catch {}
1414
- throw new Error(`cannot resolve asset path for ${assetPath}`);
1415
- }
1416
- /**
1417
- * @see https://github.com/facebook/react-native/blob/0.83-stable/packages/community-cli-plugin/src/commands/bundle/assetPathUtils.js
1418
- */
1419
- async function copyAssetsToDestination(options) {
1420
- const { assets, platform, assetsDir, preferNativePlatform } = options;
1421
- const context = {
1422
- platform,
1423
- preferNativePlatform
1424
- };
1425
- const mkdirWithAssertPath = (targetPath) => {
1426
- const dirname = path.dirname(targetPath);
1427
- fs.mkdirSync(dirname, { recursive: true });
1428
- };
1429
- return Promise.all(assets.map((asset) => {
1430
- return Promise.all(asset.scales.map(async (scale) => {
1431
- if (platform !== "android") {
1432
- const from = resolveAssetPath(asset.id, context, scale);
1433
- const to = path.join(assetsDir, getIosAssetDestinationPath(asset, scale));
1434
- mkdirWithAssertPath(to);
1435
- return fs.copyFileSync(from, to);
1436
- }
1437
- const from = resolveAssetPath(asset.id, context, scale);
1438
- const to = path.join(assetsDir, getAndroidAssetDestinationPath(asset, scale));
1439
- mkdirWithAssertPath(to);
1440
- fs.copyFileSync(from, to);
1441
- })).then(() => void 0);
1442
- })).then(() => void 0);
1443
- }
1444
- /**
1445
- * @see https://github.com/facebook/react-native/blob/0.83-stable/packages/community-cli-plugin/src/commands/bundle/getAssetDestPathIOS.js
1446
- */
1447
- function getIosAssetDestinationPath(asset, scale) {
1448
- const suffix = scale === 1 ? "" : `@${scale}x`;
1449
- const fileName = `${asset.name + suffix}.${asset.type}`;
1450
- const devServerBasePath = asset.httpServerLocation.at(0) === "/" ? asset.httpServerLocation.slice(1) : asset.httpServerLocation;
1451
- return path.join(devServerBasePath.replace(/\.\.\//g, "_"), fileName);
1452
- }
1453
- function getAndroidAssetDestinationPath(asset, scale) {
1454
- const assetQualifierSuffix = ANDROID_ASSET_QUALIFIER[scale];
1455
- const assetName = `${asset.httpServerLocation.at(0) === "/" ? asset.httpServerLocation.slice(1) : asset.httpServerLocation}/${asset.name}`.toLowerCase().replace(/\//g, "_").replace(/(?:[^a-z0-9_])/g, "").replace(/^assets_/, "");
1456
- if (!assetQualifierSuffix) throw new Error(`invalid asset qualifier: ${asset.id}`);
1457
- return path.join(isDrawable(asset.type) ? `drawable-${assetQualifierSuffix}` : "raw", `${assetName}.${asset.type}`);
1458
- }
1459
- /**
1460
- * @see https://developer.android.com/guide/topics/resources/drawable-resource
1461
- */
1462
- function isDrawable(type) {
1463
- return [
1464
- "gif",
1465
- "heic",
1466
- "heif",
1467
- "jpeg",
1468
- "jpg",
1469
- "ktx",
1470
- "png",
1471
- "webp",
1472
- "xml"
1473
- ].includes(type);
1474
- }
1475
- function generateAssetRegistryCode(assetRegistryPath, asset) {
1476
- return `module.exports = require('${assetRegistryPath}').registerAsset(${JSON.stringify(asset)});`;
1477
- }
1478
- //#endregion
1479
- //#region src/core/plugins/utils/transform-utils.ts
1480
- const TRANSFORM_FLAGS_KEY = Symbol("transform-flags");
1481
- let TransformFlag = /* @__PURE__ */ function(TransformFlag) {
1482
- TransformFlag[TransformFlag["NONE"] = 0] = "NONE";
1483
- TransformFlag[TransformFlag["CODEGEN_REQUIRED"] = 1] = "CODEGEN_REQUIRED";
1484
- TransformFlag[TransformFlag["STRIP_FLOW_REQUIRED"] = 2] = "STRIP_FLOW_REQUIRED";
1485
- TransformFlag[TransformFlag["SKIP_ALL"] = 128] = "SKIP_ALL";
1486
- return TransformFlag;
1487
- }({});
1488
- function setFlag(context, id, flag, options) {
1489
- const moduleInfo = context.getModuleInfo(id);
1490
- if (moduleInfo && hasFlag(moduleInfo.meta)) {
1491
- if (options?.override) moduleInfo.meta[TRANSFORM_FLAGS_KEY] = flag;
1492
- else moduleInfo.meta[TRANSFORM_FLAGS_KEY] |= flag;
1493
- return moduleInfo.meta;
1494
- } else return { [TRANSFORM_FLAGS_KEY]: flag };
1495
- }
1496
- function hasFlag(meta) {
1497
- return TRANSFORM_FLAGS_KEY in meta;
1498
- }
1499
- function getFlag(context, id) {
1500
- return getFlagFromModuleInfo(context.getModuleInfo(id));
1501
- }
1502
- function getFlagFromModuleInfo(moduleInfo) {
1503
- if (moduleInfo && hasFlag(moduleInfo.meta)) return moduleInfo.meta[TRANSFORM_FLAGS_KEY];
1504
- return TransformFlag.NONE;
1505
- }
1506
- function withTransformBoundary(context, plugins) {
1507
- return [
1508
- {
1509
- name: "rollipop:transform-initializer",
1510
- transform: {
1511
- order: "pre",
1512
- handler(_code, id) {
1513
- if (context.state.hmrUpdates.has(id)) {
1514
- context.state.hmrUpdates.delete(id);
1515
- return { meta: setFlag(this, id, TransformFlag.NONE, { override: true }) };
1516
- }
1517
- }
1518
- }
1519
- },
1520
- {
1521
- name: "rollipop:transform-change-watcher",
1522
- watchChange(id) {
1523
- context.state.hmrUpdates.add(id);
1524
- }
1525
- },
1526
- plugins
1527
- ];
1528
- }
1529
- //#endregion
1530
- //#region src/core/plugins/react-native-plugin.ts
1531
- function reactNativePlugin(options) {
1532
- const { projectRoot, platform, preferNativePlatform, buildType, assetsDir, assetExtensions, assetRegistryPath, flowFilter, codegenFilter, builtinPluginConfig } = options;
1533
- const codegenPlugin = {
1534
- name: "rollipop:react-native-codegen-marker",
1535
- transform: {
1536
- order: "pre",
1537
- filter: codegenFilter,
1538
- handler(_code, id) {
1539
- return { meta: setFlag(this, id, TransformFlag.CODEGEN_REQUIRED) };
1540
- }
1541
- }
1542
- };
1543
- const stripFlowSyntaxPlugin = {
1544
- name: "rollipop:react-native-strip-flow-syntax",
1545
- transform: {
1546
- order: "pre",
1547
- filter: flowFilter,
1548
- async handler(code, id) {
1549
- const flags = getFlag(this, id);
1550
- if (flags & TransformFlag.SKIP_ALL) return;
1551
- if (flags & TransformFlag.CODEGEN_REQUIRED) return { meta: setFlag(this, id, TransformFlag.STRIP_FLOW_REQUIRED) };
1552
- const result = await stripFlowTypes(id, code);
1553
- return {
1554
- code: result.code,
1555
- map: result.map,
1556
- moduleType: "tsx"
1557
- };
1558
- }
1559
- }
1560
- };
1561
- const assets = [];
1562
- const assetPlugin = {
1563
- name: "rollipop:react-native-asset",
1564
- load: {
1565
- filter: [include(id(new RegExp(`\\.(?:${assetExtensions.join("|")})$`)))],
1566
- async handler(id) {
1567
- this.debug(`Asset ${id} found`);
1568
- const assetData = await resolveScaledAssets({
1569
- projectRoot,
1570
- assetPath: id,
1571
- platform,
1572
- preferNativePlatform
1573
- });
1574
- assets.push(assetData);
1575
- return {
1576
- code: generateAssetRegistryCode(assetRegistryPath, assetData),
1577
- meta: setFlag(this, id, TransformFlag.SKIP_ALL),
1578
- moduleType: "js"
1579
- };
1580
- }
1581
- },
1582
- buildStart() {
1583
- assets.length = 0;
1584
- },
1585
- async buildEnd(error) {
1586
- if (error || buildType === "serve") return;
1587
- if (assetsDir != null) {
1588
- this.debug(`Copying assets to ${assetsDir}`);
1589
- await copyAssetsToDestination({
1590
- assets,
1591
- assetsDir,
1592
- platform,
1593
- preferNativePlatform
1594
- });
1595
- }
1596
- }
1597
- };
1598
- return [...builtinPluginConfig ? [rollipopReactNativePlugin(builtinPluginConfig)] : [codegenPlugin, stripFlowSyntaxPlugin], assetPlugin];
1599
- }
1600
- //#endregion
1601
- //#region src/core/plugins/prelude-plugin.ts
1602
- const IS_ENTRY = Symbol("IS_ENTRY");
1603
- function preludePlugin(options) {
1604
- if (options.modulePaths.length === 0) return null;
1605
- const preludeImportStatements = options.modulePaths.map((modulePath) => `import '${modulePath}';`).join("\n");
1606
- let processed = false;
1607
- return {
1608
- name: "rollipop:prelude",
1609
- buildStart() {
1610
- processed = false;
1611
- },
1612
- resolveId: { handler: (source, _importer, extraOptions) => {
1613
- if (extraOptions.isEntry) return {
1614
- id: source,
1615
- meta: { [IS_ENTRY]: true }
1616
- };
1617
- } },
1618
- load: { handler(id) {
1619
- if (processed) return;
1620
- const moduleInfo = this.getModuleInfo(id);
1621
- if (moduleInfo && isEntry(moduleInfo.meta)) {
1622
- this.debug(`Prelude plugin found entry ${id}`);
1623
- const modifiedSource = [preludeImportStatements, fs.readFileSync(id, "utf-8")].join("\n");
1624
- processed = true;
1625
- return modifiedSource;
1626
- }
1627
- } }
1628
- };
1629
- }
1630
- function isEntry(meta) {
1631
- return IS_ENTRY in meta;
1632
- }
1633
- //#endregion
1634
- //#region src/core/plugins/json-plugin.ts
1635
- function jsonPlugin() {
1636
- return {
1637
- name: "rollipop:json",
1638
- load: {
1639
- filter: [include(id(/\.json$/))],
1640
- handler(id) {
1641
- return {
1642
- code: `export = ${fs.readFileSync(id, "utf-8")};`,
1643
- meta: setFlag(this, id, TransformFlag.SKIP_ALL),
1644
- moduleType: "ts"
1645
- };
1646
- }
1647
- }
1648
- };
1649
- }
1650
- //#endregion
1651
- //#region src/utils/babel.ts
1652
- function mergeBabelOptions(options) {
1653
- return options.reduce((acc, options) => mergeWith(acc, options, merge$2), {});
1654
- }
1655
- function merge$2(target, source, key) {
1656
- if (key === "plugins") return [...target ?? [], ...source ?? []];
1657
- if (key === "presets") return [...target ?? [], ...source ?? []];
1658
- }
1659
- //#endregion
1660
- //#region src/core/plugins/utils/source.ts
1661
- const TS_EXTENSION_REGEXP = /\.tsx?$/;
1662
- function isTS(id) {
1663
- return TS_EXTENSION_REGEXP.test(id);
1664
- }
1665
- function isJSX(id) {
1666
- return id.endsWith("x");
1667
- }
1668
- //#endregion
1669
- //#region src/core/plugins/babel-plugin.ts
1670
- function babelPlugin({ useNativeTransformPipeline, transformConfig }) {
1671
- const { rules = [] } = transformConfig ?? {};
1672
- const babelOptionsById = /* @__PURE__ */ new Map();
1673
- const babelRules = rules.map(({ filter, options }, index) => {
1674
- return {
1675
- name: `rollipop:babel-rule-${index}`,
1676
- transform: {
1677
- filter,
1678
- handler(code, id) {
1679
- const existingBabelOptions = babelOptionsById.get(id);
1680
- const resolvedOptions = typeof options === "function" ? options(code, id) : options;
1681
- existingBabelOptions ? existingBabelOptions.push(resolvedOptions) : babelOptionsById.set(id, [resolvedOptions]);
1682
- }
1683
- }
1684
- };
1685
- });
1686
- const babelPlugin = {
1687
- name: "rollipop:babel",
1688
- buildStart() {
1689
- babelOptionsById.clear();
1690
- },
1691
- transform: { handler(code, id) {
1692
- const flags = getFlag(this, id);
1693
- if (flags & TransformFlag.SKIP_ALL) return;
1694
- const babelOptions = babelOptionsById.get(id) ?? [];
1695
- if (!(useNativeTransformPipeline ? babelOptions.length > 0 : flags & TransformFlag.CODEGEN_REQUIRED || babelOptions.length > 0)) return;
1696
- const baseOptions = useNativeTransformPipeline ? [] : [getPreset(flags, id)];
1697
- const result = babel.transformSync(code, {
1698
- filename: id,
1699
- babelrc: false,
1700
- configFile: false,
1701
- sourceMaps: true,
1702
- ...mergeBabelOptions([...baseOptions, ...babelOptions])
1703
- });
1704
- invariant(result?.code, `Failed to transform with babel: ${id}`);
1705
- return {
1706
- code: result.code,
1707
- map: result.map
1708
- };
1709
- } }
1710
- };
1711
- return [...babelRules, babelPlugin];
1712
- }
1713
- function getPreset(flags, id) {
1714
- const presets = [];
1715
- const plugins = [];
1716
- let parserOpts = null;
1717
- if (flags & TransformFlag.STRIP_FLOW_REQUIRED) {
1718
- parserOpts = { flow: "all" };
1719
- plugins.push([__require.resolve("babel-plugin-syntax-hermes-parser"), {
1720
- parseLangTypes: "flow",
1721
- reactRuntimeTarget: "19"
1722
- }], __require.resolve("@babel/plugin-transform-flow-strip-types"));
1723
- } else if (isTS(id)) plugins.push([__require.resolve("@babel/plugin-transform-typescript"), {
1724
- isTSX: isJSX(id),
1725
- allowNamespaces: true
1726
- }]);
1727
- if (flags & TransformFlag.CODEGEN_REQUIRED) plugins.push([__require.resolve("@react-native/babel-plugin-codegen")]);
1728
- const options = {
1729
- presets,
1730
- plugins
1731
- };
1732
- if (parserOpts) options.parserOpts = parserOpts;
1733
- return options;
1734
- }
1735
- //#endregion
1736
- //#region src/utils/swc.ts
1737
- function mergeSwcOptions(options) {
1738
- return options.reduce((acc, options) => mergeWith(acc, options, merge$1), {});
1739
- }
1740
- function merge$1(target, source, key) {
1741
- if (key === "plugins") return [...target ?? [], ...source ?? []];
1742
- }
1743
- //#endregion
1744
- //#region src/core/plugins/shared/filters.ts
1745
- const ROLLDOWN_RUNTIME_EXCLUDE_FILTER = exclude(or(id(/rolldown\/runtime/), id(/@oxc-project\+runtime/)));
1746
- //#endregion
1747
- //#region src/core/plugins/swc-plugin.ts
1748
- function swcPlugin({ useNativeTransformPipeline, runtimeTarget, transformConfig }) {
1749
- const { rules = [] } = transformConfig ?? {};
1750
- const swcOptionsById = /* @__PURE__ */ new Map();
1751
- const swcHelpersResolvePlugin = {
1752
- name: "rollipop:swc-helpers-resolve",
1753
- resolveId: {
1754
- order: "pre",
1755
- filter: [include(id(/^@swc\/helpers/)), ROLLDOWN_RUNTIME_EXCLUDE_FILTER],
1756
- handler(source, _importer, extraOptions) {
1757
- return this.resolve(source, import.meta.dirname, extraOptions);
1758
- }
1759
- }
1760
- };
1761
- const swcRules = rules.map(({ filter, options }, index) => {
1762
- return {
1763
- name: `rollipop:swc-rule-${index}`,
1764
- transform: {
1765
- filter,
1766
- handler(code, id) {
1767
- const existingBabelOptions = swcOptionsById.get(id);
1768
- const resolvedOptions = typeof options === "function" ? options(code, id) : options;
1769
- existingBabelOptions ? existingBabelOptions.push(resolvedOptions) : swcOptionsById.set(id, [resolvedOptions]);
1770
- }
1771
- }
1772
- };
1773
- });
1774
- const getSwcPreset = useNativeTransformPipeline ? null : presets[runtimeTarget];
1775
- const swcPlugin = {
1776
- name: "rollipop:swc",
1777
- buildStart() {
1778
- swcOptionsById.clear();
1779
- },
1780
- transform: {
1781
- filter: [ROLLDOWN_RUNTIME_EXCLUDE_FILTER],
1782
- handler(code, id) {
1783
- if (getFlag(this, id) & TransformFlag.SKIP_ALL) return;
1784
- const swcOptions = swcOptionsById.get(id) ?? [];
1785
- if (getSwcPreset == null && swcOptions.length === 0) return;
1786
- const baseOptions = getSwcPreset != null ? [getSwcPreset(id)] : [];
1787
- const result = swc.transformSync(code, {
1788
- filename: id,
1789
- configFile: false,
1790
- swcrc: false,
1791
- sourceMaps: true,
1792
- inputSourceMap: false,
1793
- ...mergeSwcOptions([...baseOptions, ...swcOptions])
1794
- });
1795
- return {
1796
- code: result.code,
1797
- map: result.map
1798
- };
1799
- }
1800
- }
1801
- };
1802
- return [
1803
- swcHelpersResolvePlugin,
1804
- ...swcRules,
1805
- swcPlugin
1806
- ];
1807
- }
1808
- const presets = {
1809
- "hermes-v1": (id) => ({
1810
- env: {
1811
- targets: { node: 9999 },
1812
- include: [
1813
- "transform-block-scoping",
1814
- "transform-class-properties",
1815
- "transform-private-methods",
1816
- "transform-private-property-in-object"
1817
- ]
1818
- },
1819
- jsc: {
1820
- parser: {
1821
- syntax: "typescript",
1822
- tsx: true
1823
- },
1824
- transform: { react: { runtime: "preserve" } },
1825
- externalHelpers: true
1826
- },
1827
- isModule: id.endsWith(".cjs") ? "commonjs" : true
1828
- }),
1829
- hermes: (id) => ({
1830
- jsc: {
1831
- parser: {
1832
- syntax: "typescript",
1833
- tsx: true
1834
- },
1835
- transform: { react: { runtime: "preserve" } },
1836
- externalHelpers: true,
1837
- keepClassNames: true,
1838
- loose: false,
1839
- assumptions: {
1840
- setPublicClassFields: true,
1841
- privateFieldsAsProperties: true
1842
- },
1843
- target: "es5"
1844
- },
1845
- isModule: id.endsWith(".cjs") ? "commonjs" : true
1846
- })
1847
- };
1848
- //#endregion
1849
- //#region src/core/plugins/reporter-plugin.ts
1850
- function reporterPlugin(options) {
1851
- const { reporter, initialTotalModules = 0 } = options ?? {};
1852
- let totalModules = initialTotalModules;
1853
- let startedAt = 0;
1854
- let transformedModules = 0;
1855
- let unknownTotalModules = totalModules === 0;
1856
- return {
1857
- name: "rollipop:status",
1858
- buildStart() {
1859
- startedAt = performance.now();
1860
- transformedModules = 0;
1861
- reporter?.update({ type: "bundle_build_started" });
1862
- },
1863
- buildEnd(error) {
1864
- const endedAt = performance.now();
1865
- if (transformedModules !== 0) totalModules = transformedModules;
1866
- unknownTotalModules = false;
1867
- reporter?.update(error == null ? {
1868
- type: "bundle_build_done",
1869
- totalModules,
1870
- duration: endedAt - startedAt
1871
- } : {
1872
- type: "bundle_build_failed",
1873
- error
1874
- });
1875
- },
1876
- transform: {
1877
- order: "post",
1878
- handler(_code, id) {
1879
- ++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
- });
1887
- }
1888
- },
1889
- watchChange(id) {
1890
- reporter?.update({
1891
- type: "watch_change",
1892
- id
1893
- });
1894
- }
1895
- };
1896
- }
1897
- //#endregion
1898
- //#region src/core/plugins/dev-server-plugin.ts
1899
- async function devServerPlugin(options) {
1900
- const { cwd, hmrClientPath, hmrConfig } = options;
1901
- if (hmrConfig == null) return null;
1902
- return [{
1903
- name: "rollipop:replace-hmr-client",
1904
- load: {
1905
- filter: [include(id(exactRegex(resolveFrom(cwd, typeof hmrClientPath === "function" ? await hmrClientPath(cwd) : hmrClientPath))))],
1906
- handler(id) {
1907
- this.debug(`Replacing HMR client: ${id}`);
1908
- return {
1909
- code: hmrConfig.clientImplement,
1910
- moduleType: "ts"
1911
- };
1912
- }
1913
- }
1914
- }, rollipopReactRefreshWrapperPlugin({
1915
- cwd,
1916
- include: [/\.[tj]sx?(?:$|\?)/],
1917
- exclude: [/\/node_modules\//]
1918
- })];
1919
- }
1920
- //#endregion
1921
- //#region src/core/rolldown.ts
1922
- resolveRolldownOptions.cache = /* @__PURE__ */ new Map();
1923
- async function resolveRolldownOptions(context, config, buildOptions, devEngineOptions) {
1924
- const cachedOptions = resolveRolldownOptions.cache.get(context.id);
1925
- if (cachedOptions != null) return cachedOptions;
1926
- const { platform, dev, cache } = buildOptions;
1927
- const isDevServerMode = dev && context.buildType === "serve";
1928
- invariant(isDevServerMode ? devEngineOptions != null : true, "devEngineOptions is required in dev server mode");
1929
- const env = loadEnv(config);
1930
- const builtInEnv = {
1931
- MODE: config.mode,
1932
- ...isDevServerMode ? { BASE_URL: getBaseUrl(devEngineOptions.host, devEngineOptions.port, devEngineOptions.https) } : null
1933
- };
1934
- const hmrConfig = resolveHmrConfig(config);
1935
- const hmrEnabled = hmrConfig != null;
1936
- const { sourceExtensions, assetExtensions, preferNativePlatform, external: rolldownExternal, ...rolldownResolve } = config.resolver;
1937
- const { polyfills, banner: rolldownBanner, footer: rolldownFooter, postBanner: rolldownPostBanner, postFooter: rolldownPostFooter, intro: rolldownIntro, outro: rolldownOutro, shimMissingExports: rolldownShimMissingExports } = config.serializer;
1938
- const { flow: _flow, babel: _babel, swc: _swc, ...rolldownTransform } = config.transformer;
1939
- const { treeshake: rolldownTreeshake, minify: rolldownMinify, lazyBarrel: rolldownLazyBarrel, ...rolldownOptimization } = config.optimization;
1940
- const { globalIdentifiers: rolldownGlobalIdentifiers } = config.reactNative;
1941
- const { sourcemap: rolldownSourcemap, sourcemapBaseUrl: rolldownSourcemapBaseUrl, sourcemapDebugIds: rolldownSourcemapDebugIds, sourcemapIgnoreList: rolldownSourcemapIgnoreList, sourcemapPathTransform: rolldownSourcemapPathTransform } = config;
1942
- const userPlugins = config.plugins;
1943
- const mergedResolveOptions = merge({ extensions: getResolveExtensions({
1944
- sourceExtensions,
1945
- assetExtensions,
1946
- platform,
1947
- preferNativePlatform
1948
- }) }, rolldownResolve);
1949
- const mergedTransformOptions = merge({
1950
- cwd: config.root,
1951
- target: "esnext",
1952
- jsx: {
1953
- runtime: "automatic",
1954
- development: dev
1955
- },
1956
- define: {
1957
- __DEV__: asLiteral(dev),
1958
- "process.env.NODE_ENV": asLiteral(nodeEnvironment(dev)),
1959
- "process.env.DEBUG_ROLLIPOP": asLiteral(isDebugEnabled()),
1960
- ...hmrEnabled ? null : { "import.meta.hot": "{}" },
1961
- ...defineEnvFromObject(env),
1962
- ...defineEnvFromObject(builtInEnv)
1963
- },
1964
- helpers: { mode: "Runtime" }
1965
- }, rolldownTransform);
1966
- const preludePluginOptions = resolvePreludePluginOptions(config);
1967
- const reactNativePluginOptions = await resolveReactNativePluginOptions(config, context, buildOptions);
1968
- const babelPluginOptions = resolveBabelPluginOptions(config);
1969
- const swcPluginOptions = resolveSwcPluginOptions(config);
1970
- const devServerPluginOptions = resolveDevServerPluginOptions(config, hmrConfig);
1971
- const reporterPluginOptions = resolveReporterPluginOptions(config, context, buildOptions);
1972
- const finalOptions = await applyDangerouslyOverrideOptionsFinalizer(config, {
1973
- platform: "neutral",
1974
- cwd: config.root,
1975
- input: config.entry,
1976
- tsconfig: config.tsconfig,
1977
- resolve: mergedResolveOptions,
1978
- transform: mergedTransformOptions,
1979
- treeshake: rolldownTreeshake,
1980
- external: rolldownExternal,
1981
- shimMissingExports: rolldownShimMissingExports,
1982
- optimization: rolldownOptimization,
1983
- experimental: {
1984
- lazyBarrel: rolldownLazyBarrel,
1985
- ...isDevServerMode ? { devMode: hmrConfig ? { implement: hmrConfig.runtimeImplement } : false } : null
1986
- },
1987
- plugins: withTransformBoundary(context, [
1988
- preludePlugin(preludePluginOptions),
1989
- reactNativePlugin(reactNativePluginOptions),
1990
- jsonPlugin(),
1991
- babelPlugin(babelPluginOptions),
1992
- swcPlugin(swcPluginOptions),
1993
- devServerPlugin(devServerPluginOptions),
1994
- reporterPlugin(reporterPluginOptions),
1995
- userPlugins
1996
- ]),
1997
- checks: {
1998
- eval: false,
1999
- pluginTimings: isDebugEnabled()
2000
- },
2001
- logLevel: isDebugEnabled() ? "debug" : "info",
2002
- onLog(level, log, defaultHandler) {
2003
- if (log.code?.startsWith("PLUGIN_")) printPluginLog(level, log, log.plugin);
2004
- else defaultHandler(level, log);
2005
- },
2006
- id: context.id
2007
- }, {
2008
- format: "esm",
2009
- file: buildOptions.outfile,
2010
- banner: rolldownBanner,
2011
- footer: rolldownFooter,
2012
- postFooter: rolldownPostFooter,
2013
- postBanner: rolldownPostBanner,
2014
- outro: rolldownOutro,
2015
- intro: async (chunk) => {
2016
- return [
2017
- ...getGlobalVariables(dev, context.buildType),
2018
- ...loadPolyfills(polyfills),
2019
- typeof rolldownIntro === "function" ? await rolldownIntro(chunk) : rolldownIntro
2020
- ].filter(isNotNil).join("\n");
2021
- },
2022
- minify: buildOptions.minify ?? rolldownMinify,
2023
- sourcemap: buildOptions.sourcemap ?? rolldownSourcemap,
2024
- sourcemapBaseUrl: rolldownSourcemapBaseUrl,
2025
- sourcemapDebugIds: rolldownSourcemapDebugIds,
2026
- sourcemapIgnoreList: rolldownSourcemapIgnoreList,
2027
- sourcemapPathTransform: rolldownSourcemapPathTransform ?? createProjectRootSourcemapPathTransform(config.root),
2028
- codeSplitting: false,
2029
- strictExecutionOrder: true,
2030
- globalIdentifiers: rolldownGlobalIdentifiers,
2031
- persistentCache: cache
2032
- });
2033
- resolveRolldownOptions.cache.set(context.id, finalOptions);
2034
- return finalOptions;
2035
- }
2036
- function resolvePreludePluginOptions(config) {
2037
- return { modulePaths: config.serializer.prelude };
2038
- }
2039
- async function resolveReactNativePluginOptions(config, context, buildOptions) {
2040
- return {
2041
- projectRoot: config.root,
2042
- platform: buildOptions.platform,
2043
- preferNativePlatform: config.resolver.preferNativePlatform,
2044
- buildType: context.buildType,
2045
- assetsDir: buildOptions.assetsDir,
2046
- assetExtensions: config.resolver.assetExtensions,
2047
- assetRegistryPath: await resolveAssetRegistryPath(config),
2048
- flowFilter: config.transformer.flow?.filter ?? [],
2049
- codegenFilter: config.reactNative.codegen?.filter ?? [],
2050
- builtinPluginConfig: resolveReactNativeBuiltinPluginConfig(config)
2051
- };
2052
- }
2053
- async function resolveAssetRegistryPath(config) {
2054
- const { assetRegistryPath } = config.reactNative;
2055
- const path = typeof assetRegistryPath === "function" ? await assetRegistryPath(config.root) : assetRegistryPath;
2056
- return resolveFrom(config.root, path);
2057
- }
2058
- function resolveReactNativeBuiltinPluginConfig(config) {
2059
- if (!config.experimental?.nativeTransformPipeline) return null;
2060
- return {
2061
- envName: config.mode,
2062
- runtimeTarget: resolveRuntimeTarget(config.runtimeTarget),
2063
- flow: config.experimental.flow,
2064
- worklets: resolveWorkletsConfig(config)
2065
- };
2066
- }
2067
- function resolveWorkletsConfig(config) {
2068
- const { worklets } = config.experimental ?? {};
2069
- if (worklets == null) return;
2070
- return merge({
2071
- isRelease: config.mode === "production",
2072
- pluginVersion: resolvePackageJson(config.root, "react-native-worklets")?.version
2073
- }, worklets);
2074
- }
2075
- function resolveBabelPluginOptions(config) {
2076
- return {
2077
- useNativeTransformPipeline: config.experimental?.nativeTransformPipeline,
2078
- transformConfig: config.transformer.babel
2079
- };
2080
- }
2081
- function resolveSwcPluginOptions(config) {
2082
- return {
2083
- useNativeTransformPipeline: config.experimental?.nativeTransformPipeline,
2084
- runtimeTarget: config.runtimeTarget,
2085
- transformConfig: config.transformer.swc
2086
- };
2087
- }
2088
- function resolveDevServerPluginOptions(config, hmrConfig) {
2089
- return {
2090
- cwd: config.root,
2091
- hmrClientPath: config.reactNative.hmrClientPath,
2092
- hmrConfig
2093
- };
2094
- }
2095
- function resolveReporterPluginOptions(config, context, buildOptions) {
2096
- const statusReporter = createStatusReporter(config, context, buildOptions);
2097
- return {
2098
- initialTotalModules: getBuildTotalModules(context.storage, context.id),
2099
- reporter: mergeReporters([statusReporter, config.reporter].filter(isNotNil))
2100
- };
2101
- }
2102
- function createStatusReporter(config, context, buildOptions) {
2103
- switch (config.terminal.status) {
2104
- case "compat": return new CompatStatusReporter();
2105
- case "progress": return new ProgressBarStatusReporter(context.id, `[${buildOptions.platform}, ${buildOptions.dev ? "dev" : "prod"}]`, getBuildTotalModules(context.storage, context.id));
2106
- }
2107
- }
2108
- function getResolveExtensions({ platform, sourceExtensions, assetExtensions, preferNativePlatform }) {
2109
- const supportedExtensions = [...sourceExtensions, ...assetExtensions];
2110
- return [...[platform, preferNativePlatform ? "native" : null].filter(isNotNil).map((platform) => {
2111
- return supportedExtensions.map((extension) => `.${platform}.${extension}`);
2112
- }), ...supportedExtensions.map((extension) => `.${extension}`)].flat();
2113
- }
2114
- /**
2115
- * Default sourcemap path transform.
2116
- *
2117
- * Rolldown emits `sources` relative to the bundle output's directory, which
2118
- * yields paths like `../App.tsx` when the bundle lives under e.g. `dist/`.
2119
- * RN tooling (symbolication, devtools) expects project-root-relative paths,
2120
- * so this rewrites each entry to be relative to `projectRoot`.
2121
- */
2122
- function createProjectRootSourcemapPathTransform(projectRoot) {
2123
- return (source, sourcemapPath) => {
2124
- const absolute = path.resolve(path.dirname(sourcemapPath), source);
2125
- return path.relative(projectRoot, absolute);
2126
- };
2127
- }
2128
- function loadPolyfills(polyfills) {
2129
- return polyfills.map((polyfill) => {
2130
- if (typeof polyfill === "string") return fs.readFileSync(polyfill, "utf-8");
2131
- const path = "path" in polyfill ? polyfill.path : void 0;
2132
- const content = "code" in polyfill ? polyfill.code : fs.readFileSync(polyfill.path, "utf-8");
2133
- return polyfill.type === "iife" ? iife(content, path) : content;
2134
- });
2135
- }
2136
- async function applyDangerouslyOverrideOptionsFinalizer(config, inputOptions, outputOptions) {
2137
- const override = config.dangerously_overrideRolldownOptions;
2138
- if (override == null) return {
2139
- input: inputOptions,
2140
- output: outputOptions
2141
- };
2142
- return await applyOverrideRolldownOptions(override, {
2143
- input: inputOptions,
2144
- output: outputOptions
2145
- });
2146
- }
2147
- function getOverrideOptionsForDevServer(buildOptions) {
2148
- return {
2149
- input: {
2150
- transform: { jsx: { development: true } },
2151
- experimental: {
2152
- incrementalBuild: true,
2153
- nativeMagicString: true
2154
- },
2155
- treeshake: false
2156
- },
2157
- output: {
2158
- minify: buildOptions.minify ?? false,
2159
- sourcemap: buildOptions.sourcemap ?? true,
2160
- generatedCode: {
2161
- symbols: buildOptions.dev,
2162
- profilerNames: buildOptions.dev
2163
- }
2164
- }
2165
- };
2166
- }
2167
- //#endregion
2168
- //#region src/core/bundler.ts
2169
- var Bundler = class Bundler {
2170
- static async devEngine(config, buildOptions, devEngineOptions) {
2171
- const buildType = "serve";
2172
- const resolvedBuildOptions = resolveBuildOptions(config, buildOptions);
2173
- const context = Bundler.createContext(buildType, config, resolvedBuildOptions);
2174
- const { input = {}, output = {} } = await resolveRolldownOptions(context, config, resolvedBuildOptions, devEngineOptions);
2175
- const devServerOptions = getOverrideOptionsForDevServer(resolvedBuildOptions);
2176
- const devEngine = await dev(merge(input, devServerOptions.input), merge(output, devServerOptions.output), {
2177
- watch: config.watcher,
2178
- ...devEngineOptions
2179
- });
2180
- Object.defineProperty(devEngine, "getContext", {
2181
- value: () => context,
2182
- enumerable: true,
2183
- configurable: false
2184
- });
2185
- return devEngine;
2186
- }
2187
- static createId(config, buildOptions) {
2188
- return createId(config, buildOptions);
2189
- }
2190
- static createContext(buildType, config, buildOptions) {
2191
- return {
2192
- id: Bundler.createId(config, buildOptions),
2193
- root: config.root,
2194
- storage: FileStorage.getInstance(config.root),
2195
- buildType,
2196
- state: { hmrUpdates: /* @__PURE__ */ new Set() }
2197
- };
2198
- }
2199
- constructor(config) {
2200
- this.config = config;
2201
- Logo.printOnce();
2202
- }
2203
- async build(buildOptions) {
2204
- const buildType = "build";
2205
- const resolvedBuildOptions = resolveBuildOptions(this.config, buildOptions);
2206
- const context = Bundler.createContext(buildType, this.config, resolvedBuildOptions);
2207
- const sourcemap = resolvedBuildOptions.sourcemap ? true : false;
2208
- const { input, output } = await resolveRolldownOptions(context, this.config, resolvedBuildOptions);
2209
- const rolldownBuildOptions = {
2210
- ...input,
2211
- output: {
2212
- ...output,
2213
- sourcemap
2214
- },
2215
- write: Boolean(resolvedBuildOptions.outfile)
2216
- };
2217
- const chunk = (await rolldown.build(rolldownBuildOptions)).output[0];
2218
- invariant(chunk, "Bundled chunk is not found");
2219
- if (resolvedBuildOptions.outfile && chunk.sourcemapFileName && resolvedBuildOptions.sourcemapOutfile) {
2220
- const outputDir = path.dirname(resolvedBuildOptions.outfile);
2221
- const sourcemapDir = path.dirname(resolvedBuildOptions.sourcemapOutfile);
2222
- const sourcemapFile = path.join(outputDir, chunk.sourcemapFileName);
2223
- if (!fs.existsSync(sourcemapDir)) fs.mkdirSync(sourcemapDir, { recursive: true });
2224
- fs.renameSync(sourcemapFile, resolvedBuildOptions.sourcemapOutfile);
2225
- }
2226
- return chunk;
2227
- }
2228
- };
2229
- //#endregion
2230
- //#region src/utils/bundle.ts
2231
- function getBaseBundleName(name) {
2232
- return name.replace(/^\//, "").replace(/\.bundle$/, "");
2233
- }
2234
- //#endregion
2235
- //#region src/utils/errors.ts
2236
- function normalizeRolldownError(error) {
2237
- const normalizedError = new Error(stripAnsi(error.message));
2238
- normalizedError.stack = error.stack;
2239
- return normalizedError;
2240
- }
2241
- //#endregion
2242
- //#region src/utils/promise.ts
2243
- function taskHandler() {
2244
- let resolver;
2245
- let rejector;
2246
- return {
2247
- task: new Promise((resolve, reject) => {
2248
- resolver = resolve;
2249
- rejector = reject;
2250
- }),
2251
- resolve: () => resolver?.(void 0),
2252
- reject: (reason) => rejector?.(reason)
2253
- };
2254
- }
2255
- //#endregion
2256
- //#region src/server/bundle.ts
2257
- var FileSystemBundleStore = class {
2258
- bundleFilePath;
2259
- _sourceMap;
2260
- lazySourceMapConsumer = null;
2261
- holder;
2262
- constructor(projectRoot, id, code, sourceMap) {
2263
- const sharedDataPath = getSharedDataPath(projectRoot);
2264
- const bundlesPath = path.join(sharedDataPath, "bundles");
2265
- const bundleFilePath = path.join(bundlesPath, `${id}.bundle`);
2266
- if (!fs.existsSync(bundlesPath)) fs.mkdirSync(bundlesPath, { recursive: true });
2267
- fs.writeFileSync(bundleFilePath, code, { encoding: "utf-8" });
2268
- const stats = fs.statSync(bundleFilePath);
2269
- this.bundleFilePath = bundleFilePath;
2270
- this._sourceMap = sourceMap;
2271
- this.holder = {
2272
- code,
2273
- mtimeMs: stats.mtimeMs
2274
- };
2275
- logger.debug(`File system bundle created at ${bundleFilePath}`);
2276
- }
2277
- update() {
2278
- this.holder = {
2279
- code: fs.readFileSync(this.bundleFilePath, { encoding: "utf-8" }),
2280
- mtimeMs: fs.statSync(this.bundleFilePath).mtimeMs
2281
- };
2282
- }
2283
- get code() {
2284
- if (this.isStale()) {
2285
- logger.info("File system bundle is stale, updating...");
2286
- this.update();
2287
- } else logger.trace("File system bundle is up to date");
2288
- return this.holder.code;
2289
- }
2290
- get sourceMap() {
2291
- return this.isStale() ? void 0 : this._sourceMap;
2292
- }
2293
- get sourceMapConsumer() {
2294
- if (this.isStale() || this._sourceMap == null) return;
2295
- if (this.lazySourceMapConsumer == null) this.lazySourceMapConsumer = new SourceMapConsumer(this._sourceMap);
2296
- return this.lazySourceMapConsumer;
2297
- }
2298
- isStale() {
2299
- return this.holder.mtimeMs !== fs.statSync(this.bundleFilePath).mtimeMs;
2300
- }
2301
- };
2302
- //#endregion
2303
- //#region src/server/bundler-pool.ts
2304
- var BundlerDevEngine = class extends EventEmitter {
2305
- initializeHandle;
2306
- isHmrEnabled;
2307
- _id;
2308
- bundleStore = null;
2309
- buildFailedError = null;
2310
- _devEngine = null;
2311
- _state = "idle";
2312
- _status = "idle";
2313
- constructor(options, config, buildOptions, onReporterEvent) {
2314
- super();
2315
- this.options = options;
2316
- this.config = config;
2317
- this.buildOptions = buildOptions;
2318
- this.onReporterEvent = onReporterEvent;
2319
- this._id = Bundler.createId(config, buildOptions);
2320
- this.initializeHandle = taskHandler();
2321
- 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
- this.initialize();
2332
- }
2333
- get id() {
2334
- return this._id;
2335
- }
2336
- /** Snapshot of the bundler's current lifecycle state. */
2337
- get status() {
2338
- return this._status;
2339
- }
2340
- get devEngine() {
2341
- invariant(this._devEngine, "DevEngine is not initialized");
2342
- return this._devEngine;
2343
- }
2344
- get ensureInitialized() {
2345
- return this.initializeHandle.task;
2346
- }
2347
- async initialize() {
2348
- if (this._state !== "idle" || this._devEngine != null) return this;
2349
- 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, {
2352
- host: this.options.server.host,
2353
- port: this.options.server.port,
2354
- onHmrUpdates: (errorOrResult) => {
2355
- if (!this.isHmrEnabled) return;
2356
- if (errorOrResult instanceof Error) {
2357
- logger$1.error("Failed to handle HMR updates", {
2358
- bundlerId: this.id,
2359
- error: errorOrResult
2360
- });
2361
- const normalizedError = normalizeRolldownError(errorOrResult);
2362
- this.config.reporter?.update({
2363
- type: "bundle_build_failed",
2364
- error: normalizedError
2365
- });
2366
- } else {
2367
- logger$1.trace("Detected changed files", {
2368
- bundlerId: this.id,
2369
- changedFiles: errorOrResult.changedFiles
2370
- });
2371
- this.emit("hmrUpdates", errorOrResult.updates);
2372
- }
2373
- },
2374
- onOutput: (errorOrResult) => {
2375
- if (errorOrResult instanceof Error) {
2376
- const normalizedError = normalizeRolldownError(errorOrResult);
2377
- logger$1.trace("onOutput", { bundlerId: this.id });
2378
- logger$1.error(errorOrResult.message);
2379
- this.buildFailedError = normalizedError;
2380
- this.emit("buildFailed", normalizedError);
2381
- } else {
2382
- const output = errorOrResult.output[0];
2383
- this.updateBundleStore(output);
2384
- this.buildFailedError = null;
2385
- logger$1.debug("Build completed", {
2386
- bundlerId: this.id,
2387
- bundleName: output.name
2388
- });
2389
- }
2390
- },
2391
- rebuildStrategy: "auto"
2392
- });
2393
- await devEngine.run();
2394
- this._devEngine = devEngine;
2395
- this._state = "ready";
2396
- this.initializeHandle.resolve();
2397
- }
2398
- updateBundleStore(output) {
2399
- this.bundleStore = new FileSystemBundleStore(this.config.root, this.id, output.code, output.map?.toString());
2400
- }
2401
- async getBundle() {
2402
- await this.ensureInitialized;
2403
- const state = await this.devEngine.getBundleState();
2404
- logger$1.debug("Bundle state", {
2405
- bundlerId: this.id,
2406
- state
2407
- });
2408
- if (state.lastFullBuildFailed) throw new Error(this.buildFailedError?.message ?? "Build failed");
2409
- if (state.hasStaleOutput || this.bundleStore == null) await this.devEngine.ensureLatestBuildOutput();
2410
- invariant(this.bundleStore, "Bundle is not available");
2411
- return this.bundleStore;
2412
- }
2413
- };
2414
- var BundlerPool = class BundlerPool {
2415
- static instances = /* @__PURE__ */ new Map();
2416
- constructor(config, resolvedServerOptions, onReporterEvent) {
2417
- this.config = config;
2418
- this.resolvedServerOptions = resolvedServerOptions;
2419
- this.onReporterEvent = onReporterEvent;
2420
- }
2421
- instanceKey(bundleName, buildOptions) {
2422
- return `${bundleName}-${Bundler.createId(this.config, buildOptions)}`;
2423
- }
2424
- get(bundleName, buildOptions) {
2425
- const key = this.instanceKey(getBaseBundleName(bundleName), buildOptions);
2426
- const instance = BundlerPool.instances.get(key);
2427
- if (instance) return instance;
2428
- else {
2429
- logger$1.debug("Preparing new bundler instance", {
2430
- bundleName,
2431
- key
2432
- });
2433
- const instance = new BundlerDevEngine({ server: this.resolvedServerOptions }, this.config, buildOptions, this.onReporterEvent);
2434
- logger$1.debug("Setting new bundler instance", { key });
2435
- BundlerPool.instances.set(key, instance);
2436
- return instance;
2437
- }
2438
- }
2439
- /**
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.
2443
- */
2444
- getInstanceById(id) {
2445
- for (const instance of BundlerPool.instances.values()) if (instance.id === id) return instance;
2446
- }
2447
- };
2448
- //#endregion
2449
- //#region src/server/constants.ts
2450
- const DEFAULT_PORT = 8081;
2451
- const DEFAULT_HOST = "localhost";
2452
- const DEV_SERVER_ASSET_PATH = "assets";
2453
- //#endregion
2454
- //#region src/server/error.ts
2455
- function errorHandler(error, request, reply) {
2456
- logger$1.error(`An error occurred while processing the request (${request.method} ${request.url}):`, error.message);
2457
- logger$1.debug(error);
2458
- reply.status(500).send("Internal Server Error");
2459
- }
2460
- //#endregion
2461
- //#region src/utils/reset-cache.ts
2462
- /**
2463
- * Resolve the build cache directory for the given project root. The cache
2464
- * itself is owned by rolldown's native implementation; we keep the path
2465
- * here only because `resetCache` — and any tooling that wants to inspect
2466
- * the on-disk layout — needs to know where to look.
2467
- */
2468
- function getCacheDirectory(projectRoot) {
2469
- return path.join(getSharedDataPath(projectRoot), "cache");
2470
- }
2471
- /**
2472
- * Remove the entire build cache directory for the given project root.
2473
- * Backs the `/reset-cache` control endpoint, the `reset_cache` MCP tool,
2474
- * and the `--reset-cache` CLI flag.
2475
- */
2476
- function resetCache(projectRoot) {
2477
- fs.rmSync(getCacheDirectory(projectRoot), {
2478
- recursive: true,
2479
- force: true
2480
- });
2481
- }
2482
- //#endregion
2483
- //#region src/server/mcp/server.ts
2484
- function createMcpServer(options) {
2485
- const { projectRoot, eventBus } = options;
2486
- const server = new McpServer({
2487
- name: "rollipop",
2488
- version: "0.1.0"
2489
- }, { capabilities: { logging: {} } });
2490
- server.registerTool("reset_cache", {
2491
- title: "Reset Cache",
2492
- description: "Clear the entire build cache. The bundler will rebuild from scratch on next change."
2493
- }, async () => {
2494
- resetCache(projectRoot);
2495
- eventBus.emit({ type: "cache_reset" });
2496
- return { content: [{
2497
- type: "text",
2498
- text: "Cache cleared successfully."
2499
- }] };
2500
- });
2501
- server.registerTool("get_build_events", {
2502
- 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.",
2504
- inputSchema: { duration: z.number().min(1e3).max(6e4).default(1e4).describe("How long to listen for events in milliseconds (1000-60000, default 10000)") }
2505
- }, async ({ duration }) => {
2506
- const events = [];
2507
- const unsubscribe = eventBus.collect(events);
2508
- await new Promise((resolve) => setTimeout(resolve, duration));
2509
- unsubscribe();
2510
- if (events.length === 0) return { content: [{
2511
- type: "text",
2512
- text: "No events received during the listening period."
2513
- }] };
2514
- return { content: [{
2515
- type: "text",
2516
- text: JSON.stringify(events, null, 2)
2517
- }] };
2518
- });
2519
- return server;
2520
- }
2521
- const sessions = /* @__PURE__ */ new Map();
2522
- const plugin$6 = fp((fastify, options) => {
2523
- fastify.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
2524
- try {
2525
- done(null, JSON.parse(body));
2526
- } catch (error) {
2527
- done(error, void 0);
2528
- }
2529
- });
2530
- fastify.post("/mcp", async (request, reply) => {
2531
- const sessionId = request.headers["mcp-session-id"];
2532
- if (sessionId && sessions.has(sessionId)) {
2533
- const { transport } = sessions.get(sessionId);
2534
- await transport.handleRequest(request.raw, reply.raw, request.body);
2535
- return reply;
2536
- }
2537
- if (!sessionId && isInitializeRequest(request.body)) {
2538
- const transport = new StreamableHTTPServerTransport({
2539
- sessionIdGenerator: () => randomUUID(),
2540
- onsessioninitialized: (sid) => {
2541
- sessions.set(sid, {
2542
- transport,
2543
- server
2544
- });
2545
- }
2546
- });
2547
- transport.onclose = () => {
2548
- const sid = transport.sessionId;
2549
- if (sid) sessions.delete(sid);
2550
- };
2551
- const server = createMcpServer(options);
2552
- await server.connect(transport);
2553
- await transport.handleRequest(request.raw, reply.raw, request.body);
2554
- return reply;
2555
- }
2556
- return reply.status(400).send({
2557
- jsonrpc: "2.0",
2558
- error: {
2559
- code: -32e3,
2560
- message: "Bad Request: invalid or missing session"
2561
- },
2562
- id: null
2563
- });
2564
- });
2565
- fastify.get("/mcp", async (request, reply) => {
2566
- const sessionId = request.headers["mcp-session-id"];
2567
- if (!sessionId || !sessions.has(sessionId)) return reply.status(400).send("Missing or invalid session ID");
2568
- await sessions.get(sessionId).transport.handleRequest(request.raw, reply.raw);
2569
- return reply;
2570
- });
2571
- fastify.delete("/mcp", async (request, reply) => {
2572
- const sessionId = request.headers["mcp-session-id"];
2573
- if (!sessionId || !sessions.has(sessionId)) return reply.status(404).send("Session not found");
2574
- await sessions.get(sessionId).transport.handleRequest(request.raw, reply.raw);
2575
- return reply;
2576
- });
2577
- }, { name: "mcp" });
2578
- //#endregion
2579
- //#region src/server/middlewares/bundlers.ts
2580
- const routeParamSchema$1 = asConst({
2581
- type: "object",
2582
- required: ["id"],
2583
- properties: { id: { type: "string" } }
2584
- });
2585
- const plugin$5 = fp((fastify, { bundlerPool }) => {
2586
- fastify.get("/bundlers/:id/status", { schema: { params: routeParamSchema$1 } }, (request, reply) => {
2587
- const instance = bundlerPool.getInstanceById(request.params.id);
2588
- if (!instance) return reply.status(404).send({ error: "not found" });
2589
- return reply.send({
2590
- id: instance.id,
2591
- status: instance.status
2592
- });
2593
- });
2594
- }, { name: "bundlers" });
2595
- //#endregion
2596
- //#region src/server/middlewares/control.ts
2597
- const plugin$4 = fp((fastify, { projectRoot, eventBus }) => {
2598
- fastify.all("/reset-cache", async (_request, reply) => {
2599
- resetCache(projectRoot);
2600
- eventBus.emit({ type: "cache_reset" });
2601
- return reply.send({
2602
- success: true,
2603
- message: "Cache cleared"
2604
- });
2605
- });
2606
- }, { name: "control" });
2607
- //#endregion
2608
- //#region src/server/middlewares/request-logger.ts
2609
- const requestLogger = (req, _res, next) => {
2610
- if (isDebugEnabled()) {
2611
- logger$1.trace(chalk.bgBlue(` ${req.method} `), chalk.gray(req.url));
2612
- Object.entries(req.headers).forEach(([key, value]) => {
2613
- logger$1.trace(`${chalk.bold(key)}: ${chalk.gray(value)}`);
2614
- });
2615
- }
2616
- next();
2617
- };
2618
- //#endregion
2619
- //#region src/server/middlewares/serve-assets.ts
2620
- const queryParamSchema = asConst({
2621
- type: "object",
2622
- properties: {
2623
- platform: { type: "string" },
2624
- hash: { type: "string" }
2625
- },
2626
- required: ["platform"]
2627
- });
2628
- const plugin$3 = fp((fastify, options) => {
2629
- const { projectRoot, host, port, https, preferNativePlatform } = options;
2630
- const baseUrl = https ? `https://${host}:${port}` : `http://${host}:${port}`;
2631
- function resolveAsset(asset) {
2632
- return path.resolve(projectRoot, asset);
2633
- }
2634
- fastify.get(`/${DEV_SERVER_ASSET_PATH}/*`, {
2635
- schema: { querystring: queryParamSchema },
2636
- async handler(request, reply) {
2637
- const { url, query } = request;
2638
- const { pathname } = new URL(url, baseUrl);
2639
- const assetPath = resolveAsset(pathname.replace(new RegExp(`^/${DEV_SERVER_ASSET_PATH}/?`), ""));
2640
- let handle = null;
2641
- try {
2642
- handle = await fs.promises.open(resolveAssetPath(assetPath, {
2643
- platform: query.platform,
2644
- preferNativePlatform
2645
- }, 1), "r");
2646
- const assetData = await handle.readFile();
2647
- const { size } = await handle.stat();
2648
- await reply.header("Content-Type", mime.getType(assetPath) ?? "").header("Content-Length", size).send(assetData);
2649
- } catch (error) {
2650
- fastify.log.error(error, "Failed to serve asset (scale assets resolving is not implemented yet)");
2651
- await reply.status(500).send();
2652
- } finally {
2653
- await handle?.close();
2654
- }
2655
- }
2656
- });
2657
- }, { name: "serve-assets" });
2658
- //#endregion
2659
- //#region src/utils/response.ts
2660
- var BundleResponse = class BundleResponse {
2661
- static CRLF = "\r\n";
2662
- static THROTTLE_DELAY = 10;
2663
- done = 0;
2664
- total = 0;
2665
- boundary;
2666
- throttleTimer = null;
2667
- constructor(reply) {
2668
- this.reply = reply;
2669
- const boundary = performance.now().toString();
2670
- this.boundary = boundary;
2671
- this.reply.raw.writeHead(200, { "Content-Type": `multipart/mixed; boundary="${boundary}"` });
2672
- }
2673
- writeChunk(data, headers, end = false) {
2674
- if (this.reply.raw.writableEnded) return;
2675
- const CRLF = BundleResponse.CRLF;
2676
- this.reply.raw.write(`${CRLF}--${this.boundary}${CRLF}`);
2677
- this.reply.raw.write(Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join(CRLF) + CRLF + CRLF);
2678
- if (data) this.reply.raw.write(data);
2679
- if (end) {
2680
- this.reply.raw.write(`${CRLF}--${this.boundary}--${CRLF}`);
2681
- this.reply.raw.end();
2682
- }
2683
- }
2684
- /**
2685
- * Sample
2686
- *
2687
- * ```
2688
- * --boundary
2689
- *
2690
- * Content-Type: application/json
2691
- *
2692
- * {"done":10,"total":100}
2693
- * ```
2694
- */
2695
- writeBundleState(done, total) {
2696
- const previousProgress = this.done / this.total;
2697
- const currentProgress = done / total;
2698
- this.done = done;
2699
- this.total = total;
2700
- if (total < 10 || this.throttleTimer != null || previousProgress >= currentProgress) return;
2701
- this.writeChunk(JSON.stringify({
2702
- done,
2703
- total
2704
- }), { "Content-Type": "application/json" });
2705
- this.throttleTimer = setTimeout(() => {
2706
- this.throttleTimer = null;
2707
- }, BundleResponse.THROTTLE_DELAY);
2708
- }
2709
- /**
2710
- * Sample
2711
- *
2712
- * ```
2713
- * --boundary
2714
- *
2715
- * X-Metro-Files-Changed-Count: 0
2716
- * Content-Type: application/json
2717
- * Content-Length: 100
2718
- * Last-Modified: Thu, 10 Aug 2023 12:00:00 GMT
2719
- *
2720
- * <bundle result>
2721
- * ```
2722
- */
2723
- endWithBundle(bundle) {
2724
- this.writeChunk(JSON.stringify({
2725
- done: this.total,
2726
- total: this.total
2727
- }), { "Content-Type": "application/json" });
2728
- this.writeChunk(bundle, {
2729
- "X-Metro-Files-Changed-Count": String(0),
2730
- "Content-Type": "application/javascript; charset=UTF-8",
2731
- "Content-Length": String(Buffer.byteLength(bundle)),
2732
- "Last-Modified": (/* @__PURE__ */ new Date()).toUTCString()
2733
- }, true);
2734
- }
2735
- endWithError(error) {
2736
- const errorData = JSON.stringify({
2737
- type: error?.name ?? "InternalError",
2738
- message: error?.message ?? "internal error",
2739
- errors: []
2740
- });
2741
- this.writeChunk(errorData, {
2742
- "Content-Type": "application/json",
2743
- "X-Http-Status": "500"
2744
- }, true);
2745
- }
2746
- };
2747
- //#endregion
2748
- //#region src/server/common/schema.ts
2749
- const bundleRequestSchema = asConst({
2750
- type: "object",
2751
- properties: {
2752
- platform: { type: "string" },
2753
- app: { type: "string" },
2754
- dev: { type: "boolean" },
2755
- minify: { type: "boolean" },
2756
- runModule: { type: "boolean" },
2757
- inlineSourceMap: { type: "boolean" },
2758
- modulesOnly: { type: "boolean" }
2759
- },
2760
- required: ["platform"]
2761
- });
2762
- new Ajv().compile(bundleRequestSchema);
2763
- //#endregion
2764
- //#region src/server/middlewares/serve-bundle.ts
2765
- const routeParamSchema = asConst({
2766
- type: "object",
2767
- properties: { name: { type: "string" } }
2768
- });
2769
- function withGetBundleErrorHandler(reply, task) {
2770
- return task.catch((error) => {
2771
- return reply.status(500).send(error instanceof Error ? error.message : "Internal Server Error");
2772
- });
2773
- }
2774
- const plugin$2 = fp((fastify, options) => {
2775
- const { getBundler } = options;
2776
- const getBundleOptions = (buildOptions) => {
2777
- return {
2778
- platform: buildOptions.platform,
2779
- dev: buildOptions.dev,
2780
- minify: buildOptions.minify,
2781
- sourcemap: buildOptions.inlineSourceMap ? "inline" : true
2782
- };
2783
- };
2784
- fastify.get("/:name.bundle", {
2785
- schema: {
2786
- params: routeParamSchema,
2787
- querystring: bundleRequestSchema
2788
- },
2789
- async handler(request, reply) {
2790
- const { params, query, headers: { accept } } = request;
2791
- if (!params.name) {
2792
- await reply.status(400).send("invalid bundle name");
2793
- return;
2794
- }
2795
- const buildOptions = getBundleOptions(query);
2796
- const bundler = getBundler(params.name, buildOptions);
2797
- if (accept?.includes("multipart/mixed") ?? false) {
2798
- 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));
2804
- } else {
2805
- this.log.debug(`client is not support multipart/mixed content: ${accept ?? "<empty>"}`);
2806
- const code = (await withGetBundleErrorHandler(reply, bundler.getBundle())).code;
2807
- await reply.header("Content-Type", "application/javascript").header("Content-Length", Buffer.byteLength(code)).status(200).send(code);
2808
- }
2809
- }
2810
- });
2811
- fastify.get("/:name.map", {
2812
- schema: {
2813
- params: routeParamSchema,
2814
- querystring: bundleRequestSchema
2815
- },
2816
- async handler(request, reply) {
2817
- const { params, query } = request;
2818
- if (!params.name) {
2819
- await reply.status(400).send("invalid bundle name");
2820
- return;
2821
- }
2822
- const buildOptions = getBundleOptions(query);
2823
- const sourceMap = (await withGetBundleErrorHandler(reply, getBundler(params.name, buildOptions).getBundle())).sourceMap;
2824
- invariant(sourceMap, "Source map is not available");
2825
- await reply.header("Access-Control-Allow-Origin", "devtools://devtools").header("Content-Type", "application/json").header("Content-Length", Buffer.byteLength(sourceMap)).status(200).send(sourceMap);
2826
- }
2827
- });
2828
- }, { name: "serve-bundle" });
2829
- //#endregion
2830
- //#region src/server/middlewares/sse.ts
2831
- const plugin$1 = fp((fastify, { eventBus }) => {
2832
- fastify.get("/sse/events", (request, reply) => {
2833
- const res = reply.raw;
2834
- res.writeHead(200, {
2835
- Connection: "keep-alive",
2836
- "Content-Type": "text/event-stream",
2837
- "Cache-Control": "no-cache",
2838
- "X-Accel-Buffering": "no"
2839
- });
2840
- request.raw.socket.setNoDelay(true);
2841
- res.write(":ok\n\n");
2842
- eventBus.addClient(res);
2843
- request.raw.on("close", () => eventBus.removeClient(res));
2844
- });
2845
- }, { name: "sse" });
2846
- //#endregion
2847
- //#region src/utils/url.ts
2848
- function parseUrl(value) {
2849
- if (value.startsWith("/")) {
2850
- const [pathname, query] = value.split("?");
2851
- return {
2852
- pathname,
2853
- query: query ? toQueryObject(new URLSearchParams(query)) : {}
2854
- };
2855
- }
2856
- const url = new URL(value);
2857
- return {
2858
- pathname: url.pathname,
2859
- query: toQueryObject(url.searchParams)
2860
- };
2861
- }
2862
- function toQueryObject(searchParams) {
2863
- return searchParams.entries().reduce((acc, [key, value]) => ({
2864
- ...acc,
2865
- [key]: value
2866
- }), {});
2867
- }
2868
- //#endregion
2869
- //#region src/server/symbolicate.ts
2870
- /**
2871
- * @see https://github.com/facebook/react-native/blob/0.83-stable/packages/metro-config/src/index.flow.js#L17
2872
- */
2873
- const INTERNAL_CALLSITES_REGEX = new RegExp([
2874
- "/Libraries/BatchedBridge/MessageQueue\\.js$",
2875
- "/Libraries/Core/.+\\.js$",
2876
- "/Libraries/LogBox/.+\\.js$",
2877
- "/Libraries/Network/.+\\.js$",
2878
- "/Libraries/Pressability/.+\\.js$",
2879
- "/Libraries/Renderer/implementations/.+\\.js$",
2880
- "/Libraries/Utilities/.+\\.js$",
2881
- "/Libraries/vendor/.+\\.js$",
2882
- "/Libraries/WebSocket/.+\\.js$",
2883
- "/src/private/renderer/errorhandling/.+\\.js$",
2884
- "/metro-runtime/.+\\.js$",
2885
- "/node_modules/@babel/runtime/.+\\.js$",
2886
- "/node_modules/@react-native/js-polyfills/.+\\.js$",
2887
- "/node_modules/invariant/.+\\.js$",
2888
- "/node_modules/react-devtools-core/.+\\.js$",
2889
- "/node_modules/react-native/index.js$",
2890
- "/node_modules/react-refresh/.+\\.js$",
2891
- "/node_modules/scheduler/.+\\.js$",
2892
- "^\\[native code\\]$"
2893
- ].map((pathPattern) => pathPattern.replaceAll("/", "[/\\\\]")).join("|"));
2894
- async function symbolicate(bundleStore, stack) {
2895
- const sourceMapConsumer = await bundleStore.sourceMapConsumer;
2896
- const symbolicatedStack = stack.filter((frame) => frame.file?.startsWith("http")).map((frame) => sourceMapConsumer ? originalPositionFor(sourceMapConsumer, frame) : frame).map((frame) => collapseFrame(frame));
2897
- return {
2898
- stack: symbolicatedStack,
2899
- codeFrame: getCodeFrame(symbolicatedStack, bundleStore, sourceMapConsumer)
2900
- };
2901
- }
2902
- function originalPositionFor(sourceMapConsumer, frame) {
2903
- if (frame.column == null || frame.lineNumber == null) return frame;
2904
- const originalPosition = sourceMapConsumer.originalPositionFor({
2905
- column: frame.column,
2906
- line: frame.lineNumber
2907
- });
2908
- return Object.entries(originalPosition).reduce((frame, [key, value]) => {
2909
- const targetKey = convertFrameKey(key);
2910
- return {
2911
- ...frame,
2912
- ...value ? { [targetKey]: value } : null
2913
- };
2914
- }, frame);
2915
- }
2916
- function collapseFrame(frame) {
2917
- return {
2918
- ...frame,
2919
- collapse: Boolean(frame.file && INTERNAL_CALLSITES_REGEX.test(frame.file))
2920
- };
2921
- }
2922
- function isCollapsed(frame) {
2923
- return "collapse" in frame && frame.collapse;
2924
- }
2925
- function convertFrameKey(key) {
2926
- if (key === "line") return "lineNumber";
2927
- else if (key === "source") return "file";
2928
- else if (key === "name") return "methodName";
2929
- return key;
2930
- }
2931
- function getCodeFrame(frames, bundleStore, sourceMapConsumer) {
2932
- const frame = frames.find((frame) => {
2933
- return frame.lineNumber != null && frame.column != null && !isCollapsed(frame);
2934
- });
2935
- if (frame?.file == null || frame.column == null || frame.lineNumber == null) return null;
2936
- try {
2937
- const { lineNumber, column, file } = frame;
2938
- const unresolved = file.startsWith("http");
2939
- const source = sourceMapConsumer == null || unresolved ? bundleStore.code : sourceMapConsumer.sourceContentFor(frame.file);
2940
- const fileName = unresolved ? parseUrl(file).pathname ?? "unknown" : file;
2941
- let content = "";
2942
- if (source) content = codeFrameColumns(source, { start: {
2943
- column,
2944
- line: lineNumber
2945
- } }, { highlightCode: true });
2946
- return {
2947
- content,
2948
- fileName,
2949
- location: {
2950
- column,
2951
- row: lineNumber
2952
- }
2953
- };
2954
- } catch {
2955
- return null;
2956
- }
2957
- }
2958
- //#endregion
2959
- //#region src/server/middlewares/symbolicate.ts
2960
- const bodySchema = asConst({
2961
- type: "object",
2962
- properties: {
2963
- stack: {
2964
- type: "array",
2965
- items: {}
2966
- },
2967
- extraData: {}
2968
- },
2969
- required: ["stack"]
2970
- });
2971
- const plugin = fp((fastify, options) => {
2972
- const { getBundler } = options;
2973
- fastify.post("/symbolicate", {
2974
- schema: { body: bodySchema },
2975
- async handler(request, reply) {
2976
- const { stack } = request.body;
2977
- const bundleUrl = stack.find((frame) => frame.file?.startsWith("http"));
2978
- invariant(bundleUrl?.file, "No bundle URL found in stack frames");
2979
- const { pathname, query } = parseUrl(bundleUrl.file);
2980
- invariant(pathname, "No pathname found in bundle URL");
2981
- invariant(query.platform, "No platform found in query");
2982
- invariant(query.dev, "No dev found in query");
2983
- const symbolicateResult = await symbolicate(await getBundler(getBaseBundleName(pathname), {
2984
- platform: query.platform,
2985
- dev: query.dev === "true"
2986
- }).getBundle(), stack);
2987
- if (isDebugEnabled()) printSymbolicateResult(stack, symbolicateResult);
2988
- await reply.header("Content-Type", "application/json").send(symbolicateResult);
2989
- }
2990
- });
2991
- }, { name: "symbolicate" });
2992
- function printSymbolicateResult(rawStackFrame, symbolicateResult) {
2993
- console.log();
2994
- console.log("Symbolicate result:");
2995
- console.log();
2996
- if (symbolicateResult.codeFrame != null) {
2997
- console.log(symbolicateResult.codeFrame.content);
2998
- console.log();
2999
- }
3000
- console.log("Stack trace:");
3001
- symbolicateResult.stack.forEach((stackFrame) => {
3002
- const symbol = stackFrame.methodName ?? "<anonymous>";
3003
- const file = stackFrame.file ?? "unknown";
3004
- const location = stackFrame.lineNumber != null && stackFrame.column != null ? `(${chalk.gray.underline(`${file}:${stackFrame.lineNumber}:${stackFrame.column}`)})` : "";
3005
- console.log(` at ${symbol} ${location}`);
3006
- });
3007
- console.log();
3008
- console.log("Raw stack trace:");
3009
- rawStackFrame.filter((stackFrame) => stackFrame.file?.startsWith("http")).forEach((stackFrame) => {
3010
- const bundleName = new URL(stackFrame.file).pathname.slice(1);
3011
- const symbol = stackFrame.methodName ?? "<anonymous>";
3012
- const location = stackFrame.lineNumber != null && stackFrame.column != null ? `(${chalk.gray.underline(`${bundleName}:${stackFrame.lineNumber}:${stackFrame.column}`)})` : "";
3013
- console.log(` at ${symbol} ${location}`);
3014
- });
3015
- console.log();
3016
- }
3017
- //#endregion
3018
- //#region src/server/sse/event-bus.ts
3019
- var SSEEventBus = class {
3020
- clients = /* @__PURE__ */ new Set();
3021
- listeners = /* @__PURE__ */ new Set();
3022
- emit(event) {
3023
- const data = JSON.stringify(event);
3024
- const message = `event: ${event.type}\ndata: ${data}\n\n`;
3025
- for (const client of this.clients) if (!client.closed) client.write(message);
3026
- for (const listener of this.listeners) listener(event);
3027
- }
3028
- /**
3029
- * Subscribe an SSE HTTP response client.
3030
- */
3031
- addClient(res) {
3032
- this.clients.add(res);
3033
- }
3034
- /**
3035
- * Unsubscribe an SSE HTTP response client.
3036
- */
3037
- removeClient(res) {
3038
- this.clients.delete(res);
3039
- }
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
- get clientCount() {
3050
- return this.clients.size;
3051
- }
3052
- };
3053
- //#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
- //#region src/server/wss/server.ts
3086
- var WebSocketServer = class extends EventEmitter {
3087
- clientId = 0;
3088
- wss;
3089
- logger;
3090
- constructor(name, options) {
3091
- super();
3092
- const logger = logger$1.child(name);
3093
- const wss = new ws.WebSocketServer(options);
3094
- wss.on("connection", (socket) => {
3095
- const client = Object.defineProperty(socket, "id", {
3096
- value: this.clientId++,
3097
- writable: false
3098
- });
3099
- this.onConnection(client);
3100
- this.emit("connection", client);
3101
- client.on("message", (data) => {
3102
- this.onMessage(client, data);
3103
- this.emit("message", client, data);
3104
- });
3105
- client.on("error", (error) => {
3106
- this.onError(client, error);
3107
- this.emit("error", client, error);
3108
- });
3109
- client.on("close", () => {
3110
- this.onClose(client);
3111
- this.emit("close", client);
3112
- });
3113
- });
3114
- this.wss = wss;
3115
- this.logger = logger;
3116
- }
3117
- get server() {
3118
- return this.wss;
3119
- }
3120
- send(client, data) {
3121
- if (client.readyState === ws.WebSocket.OPEN) client.send(data);
3122
- }
3123
- sendAll(data) {
3124
- this.wss.clients.forEach((client) => {
3125
- if (client.readyState === ws.WebSocket.OPEN) client.send(data);
3126
- });
3127
- }
3128
- rawDataToString(data) {
3129
- if (Buffer.isBuffer(data)) return data.toString("utf8");
3130
- if (Array.isArray(data)) return Buffer.concat(data).toString("utf8");
3131
- return Buffer.from(data).toString("utf8");
3132
- }
3133
- };
3134
- function getWebSocketUpgradeHandler(websocketEndpoints) {
3135
- return (request, socket, head) => {
3136
- if (request.url == null) {
3137
- socket.destroy();
3138
- return;
3139
- }
3140
- const { pathname } = parseUrl(request.url);
3141
- if (pathname != null && websocketEndpoints[pathname]) {
3142
- const wss = websocketEndpoints[pathname];
3143
- wss.handleUpgrade(request, socket, head, (socket) => {
3144
- wss.emit("connection", socket, request);
3145
- });
3146
- } else socket.destroy();
3147
- };
3148
- }
3149
- //#endregion
3150
- //#region src/server/wss/hmr-server.ts
3151
- var HMRServer = class extends WebSocketServer {
3152
- bundlerPool;
3153
- reportEvent;
3154
- instances = /* @__PURE__ */ new Map();
3155
- bindings = /* @__PURE__ */ new Map();
3156
- constructor({ bundlerPool, reportEvent }) {
3157
- super("hmr", { noServer: true });
3158
- this.bundlerPool = bundlerPool;
3159
- this.reportEvent = reportEvent;
3160
- }
3161
- parseClientMessage(data) {
3162
- const parsedData = JSON.parse(this.rawDataToString(data));
3163
- const clientMessage = "type" in parsedData ? parsedData : null;
3164
- invariant(clientMessage, "Invalid HMR client message");
3165
- return clientMessage;
3166
- }
3167
- async handleConnected(client, platform, bundleEntry) {
3168
- try {
3169
- this.logger.trace(`HMR client connected (clientId: ${client.id})`, {
3170
- platform,
3171
- bundleEntry
3172
- });
3173
- const devEngineInstance = this.bundlerPool.get(bundleEntry, {
3174
- platform,
3175
- dev: true
3176
- });
3177
- this.bindEvents(client, devEngineInstance);
3178
- this.instances.set(client.id, devEngineInstance);
3179
- this.logger.trace(`Bundler instance prepared (bundlerId: ${devEngineInstance.id})`);
3180
- } catch (error) {
3181
- this.logger.error(`Failed to prepare bundler instance`, error);
3182
- }
3183
- }
3184
- bindEvents(client, instance) {
3185
- 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
3209
- });
3210
- this.logger.trace(`HMR event binding established (clientId: ${client.id})`);
3211
- }
3212
- }
3213
- async handleModuleRegistered(client, modules) {
3214
- try {
3215
- const instance = this.instances.get(client.id);
3216
- invariant(instance != null, `Bundler instance not found for client clientId: ${client.id}`);
3217
- await instance.ensureInitialized;
3218
- await instance.devEngine.registerModules(client.id.toString(), modules);
3219
- } catch (error) {
3220
- this.logger.error(`Failed to handle module registered`, error);
3221
- }
3222
- }
3223
- async handleInvalidate(client, moduleId) {
3224
- try {
3225
- const instance = this.instances.get(client.id);
3226
- invariant(instance != null, `Bundler instance not found for client clientId: ${client.id}`);
3227
- await instance.ensureInitialized;
3228
- const updates = await instance.devEngine.invalidate(moduleId);
3229
- await this.handleUpdates(client, updates);
3230
- } catch (error) {
3231
- this.logger.error(`Failed to handle invalidate`, error);
3232
- }
3233
- }
3234
- async handleUpdates(client, updates) {
3235
- this.logger.trace(`HMR updates found (clientId: ${client.id})`, { updatesCount: updates.length });
3236
- for (const clientUpdate of updates) {
3237
- const update = clientUpdate.update;
3238
- switch (update.type) {
3239
- case "Patch":
3240
- this.sendUpdateToClient(client, update);
3241
- break;
3242
- case "FullReload":
3243
- this.sendReloadToClient(client);
3244
- break;
3245
- case "Noop":
3246
- this.logger.warn(`Client ${clientUpdate.clientId} received noop update`);
3247
- break;
3248
- }
3249
- }
3250
- }
3251
- sendUpdateToClient(client, update) {
3252
- invariant(update.type === "Patch", "Invalid HMR update type");
3253
- const updateMessage = {
3254
- type: "hmr:update",
3255
- code: update.code
3256
- };
3257
- this.send(client, JSON.stringify(updateMessage));
3258
- this.done(client);
3259
- }
3260
- sendReloadToClient(client) {
3261
- this.logger.trace(`Sending HMR reload message to client (clientId: ${client.id})`);
3262
- this.send(client, JSON.stringify({ type: "hmr:reload" }));
3263
- this.done(client);
3264
- }
3265
- done(client) {
3266
- this.send(client, JSON.stringify({ type: "hmr:update-done" }));
3267
- }
3268
- sendError(client, error) {
3269
- try {
3270
- this.send(client, JSON.stringify(error));
3271
- } catch (error) {
3272
- this.logger.error(`Failed to send HMR error message to client (clientId: ${client.id})`, error);
3273
- }
3274
- }
3275
- cleanup(client) {
3276
- this.logger.trace(`HMR client cleanup (clientId: ${client.id})`);
3277
- const binding = this.bindings.get(client.id);
3278
- 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
- }
3284
- if (instance != null) try {
3285
- instance.devEngine.removeClient(String(client.id));
3286
- } catch (error) {
3287
- this.logger.warn(`Skipped devEngine.removeClient for client ${client.id}: ` + (error instanceof Error ? error.message : String(error)));
3288
- }
3289
- this.bindings.delete(client.id);
3290
- this.instances.delete(client.id);
3291
- }
3292
- onMessage(client, data) {
3293
- let message;
3294
- try {
3295
- message = this.parseClientMessage(data);
3296
- let traceMessage = message;
3297
- if (message.type === "hmr:module-registered") traceMessage = {
3298
- ...message,
3299
- modules: `[${message.modules.length} modules]`
3300
- };
3301
- else if (message.type === "hmr:log") traceMessage = {
3302
- ...message,
3303
- data: `(${message.data.length} items)`
3304
- };
3305
- this.logger.trace("HMR client message received", traceMessage);
3306
- } catch (error) {
3307
- const message = "Failed to parse HMR client message";
3308
- this.logger.error(message, error);
3309
- this.sendError(client, {
3310
- type: "InternalError",
3311
- errors: [{ description: error instanceof Error ? error.message : String(error) }],
3312
- message
3313
- });
3314
- return;
3315
- }
3316
- if (isCustomHMRMessage(message)) {
3317
- this.wss.emit(message.type, message.payload);
3318
- return;
3319
- }
3320
- switch (message.type) {
3321
- case "hmr:connected":
3322
- this.handleConnected(client, message.platform, message.bundleEntry);
3323
- break;
3324
- case "hmr:module-registered":
3325
- this.handleModuleRegistered(client, message.modules);
3326
- break;
3327
- case "hmr:invalidate":
3328
- this.handleInvalidate(client, message.moduleId);
3329
- break;
3330
- case "hmr:log":
3331
- this.reportEvent({
3332
- type: "client_log",
3333
- level: message.level,
3334
- data: message.data
3335
- });
3336
- break;
3337
- }
3338
- }
3339
- onConnection(client) {
3340
- this.logger.trace(`connection established (clientId: ${client.id})`);
3341
- }
3342
- onError(client, error) {
3343
- this.logger.error(`connection error (clientId: ${client.id})`, error);
3344
- this.cleanup(client);
3345
- }
3346
- onClose(client) {
3347
- this.logger.trace(`connection closed (clientId: ${client.id})`);
3348
- this.cleanup(client);
3349
- }
3350
- };
3351
- function isCustomHMRMessage(message) {
3352
- if (typeof message !== "object" || message == null) return false;
3353
- if ("type" in message && typeof message.type === "string" && message.type.startsWith("hmr:")) return false;
3354
- return true;
3355
- }
3356
- //#endregion
3357
- //#region src/server/create-dev-server.ts
3358
- async function createDevServer(config, options) {
3359
- const projectRoot = config.root;
3360
- const { port = DEFAULT_PORT, host = DEFAULT_HOST, https = false } = options ?? {};
3361
- if (https) throw new Error("HTTPS is not supported yet");
3362
- const serverBaseUrl = url.format({
3363
- protocol: https ? "https" : "http",
3364
- hostname: host,
3365
- port
3366
- });
3367
- await assertDevServerStatus({
3368
- devServerUrl: serverBaseUrl,
3369
- projectRoot,
3370
- port
3371
- });
3372
- const emitter = mitt();
3373
- const fastify = Fastify({
3374
- loggerInstance: new DevServerLogger(),
3375
- disableRequestLogging: true
3376
- });
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);
3384
- });
3385
- const getBundler = (bundleName, buildOptions) => {
3386
- return bundlerPool.get(bundleName, merge(options?.buildOptions ?? {}, buildOptions));
3387
- };
3388
- const { middleware: communityMiddleware, websocketEndpoints: communityWebsocketEndpoints, messageSocketEndpoint: { server: messageServer, broadcast }, eventsSocketEndpoint: { server: eventsServer, reportEvent } } = createDevServerMiddleware({
3389
- port,
3390
- host,
3391
- watchFolders: []
3392
- });
3393
- const { middleware: devMiddleware, websocketEndpoints } = createDevMiddleware({
3394
- serverBaseUrl,
3395
- logger: {
3396
- info(...args) {
3397
- if (args[0].includes("JavaScript logs have moved")) return;
3398
- logger$1.info(...args);
3399
- },
3400
- warn: logger$1.warn.bind(logger$1),
3401
- error: logger$1.error.bind(logger$1)
3402
- },
3403
- unstable_experiments: {
3404
- enableNetworkInspector: true,
3405
- enableStandaloneFuseboxShell: true
3406
- }
3407
- });
3408
- const hmrServer = new HMRServer({
3409
- 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", {
3421
- client,
3422
- data
3423
- })).on("error", (client, error) => emitter.emit("device.error", {
3424
- client,
3425
- error
3426
- })).on("close", (client) => {
3427
- emitter.emit("device.disconnected", { client });
3428
- sseEventBus.emit({
3429
- type: "device_disconnected",
3430
- clientId: client.id
3431
- });
3432
- });
3433
- await fastify.register(import("@fastify/middie"));
3434
- const devServer = {
3435
- ...emitter,
3436
- config,
3437
- instance: fastify,
3438
- middlewares: { use: fastify.use.bind(fastify) },
3439
- message: Object.assign(messageServer, { broadcast }),
3440
- events: Object.assign(eventsServer, { reportEvent }),
3441
- hot: Object.assign(hmrServer.server, {
3442
- send: (client, eventName, payload) => {
3443
- hmrServer.send(client, JSON.stringify({
3444
- type: eventName,
3445
- payload
3446
- }));
3447
- },
3448
- sendAll: (eventName, payload) => {
3449
- hmrServer.sendAll(JSON.stringify({
3450
- type: eventName,
3451
- payload
3452
- }));
3453
- }
3454
- })
3455
- };
3456
- const { invokePostConfigureServer } = await invokeConfigureServer(devServer, config.plugins ?? []);
3457
- fastify.use(requestLogger).use(communityMiddleware).use(devMiddleware).register(plugin$1, { eventBus: sseEventBus }).register(plugin$4, {
3458
- projectRoot,
3459
- eventBus: sseEventBus
3460
- }).register(plugin$5, { bundlerPool }).register(plugin$6, {
3461
- projectRoot,
3462
- eventBus: sseEventBus
3463
- }).register(plugin, { getBundler }).register(plugin$2, { getBundler }).register(plugin$3, {
3464
- projectRoot,
3465
- host,
3466
- port,
3467
- https,
3468
- preferNativePlatform: config.resolver.preferNativePlatform
3469
- }).setErrorHandler(errorHandler);
3470
- fastify.server.on("upgrade", getWebSocketUpgradeHandler({
3471
- ...communityWebsocketEndpoints,
3472
- ...websocketEndpoints,
3473
- "/hot": hmrServer.server
3474
- }));
3475
- await invokePostConfigureServer();
3476
- sseEventBus.emit({
3477
- type: "server_ready",
3478
- host,
3479
- port
3480
- });
3481
- return devServer;
3482
- }
3483
- async function invokeConfigureServer(server, plugins) {
3484
- const postConfigureServerHandlers = [];
3485
- for (const plugin of plugins) {
3486
- const context = createPluginContext(plugin.name);
3487
- const result = await plugin.configureServer?.call(context, server);
3488
- if (typeof result === "function") postConfigureServerHandlers.push(result);
3489
- }
3490
- return { invokePostConfigureServer: async () => {
3491
- for (const handler of postConfigureServerHandlers) await handler();
3492
- } };
3493
- }
3494
- //#endregion
3495
- //#region src/node/commands/agent/action.ts
3496
- const defaultBaseUrl = `http://${DEFAULT_HOST}:${DEFAULT_PORT}`;
3497
- function getAgentGuide(baseUrl = defaultBaseUrl) {
3498
- return [
3499
- "Rollipop Agent Guide",
3500
- "",
3501
- "Rollipop exposes build and runtime diagnostics through SSE and MCP.",
3502
- "Keep the dev server running, then connect your agent to one of these endpoints.",
3503
- "",
3504
- "Start the dev server:",
3505
- "",
3506
- " rollipop start --reset-cache",
3507
- "",
3508
- "Default endpoints:",
3509
- "",
3510
- ` SSE events: ${baseUrl}/sse/events`,
3511
- ` MCP server: ${baseUrl}/mcp`,
3512
- ` Reset cache: ${baseUrl}/reset-cache`,
3513
- ` Bundler status: ${baseUrl}/bundlers/<id>/status`,
3514
- "",
3515
- "If you start Rollipop with custom --host, --port, or --https options,",
3516
- "adjust these URLs to match the running dev server.",
3517
- "",
3518
- "SSE usage:",
3519
- "",
3520
- ` curl -N ${baseUrl}/sse/events`,
3521
- "",
3522
- " Event format:",
3523
- " event: <event_type>",
3524
- " data: <json_payload>",
3525
- "",
3526
- " Useful event types:",
3527
- " bundle_build_started, bundle_build_done, bundle_build_failed",
3528
- " watch_change, client_log, device_connected, device_disconnected",
3529
- " server_ready, cache_reset",
3530
- "",
3531
- "MCP setup:",
3532
- "",
3533
- " Add this to your project .mcp.json:",
3534
- "",
3535
- " {",
3536
- " \"mcpServers\": {",
3537
- " \"rollipop\": {",
3538
- " \"type\": \"http\",",
3539
- ` "url": "${baseUrl}/mcp"`,
3540
- " }",
3541
- " }",
3542
- " }",
3543
- "",
3544
- " Available tools:",
3545
- " get_build_events({ \"duration\": 10000 })",
3546
- " reset_cache()",
3547
- "",
3548
- "Agent workflow:",
3549
- "",
3550
- " 1. Run rollipop start and keep it running.",
3551
- " 2. Use get_build_events or subscribe to SSE before and after edits.",
3552
- " 3. Wait for bundle_build_done or bundle_build_failed.",
3553
- " 4. If the build fails, inspect the error payload, fix the code, and repeat.",
3554
- " 5. Use reset_cache or /reset-cache when stale cache is suspected."
3555
- ].join("\n");
3556
- }
3557
- const action$2 = async () => {
3558
- console.log(getAgentGuide());
3559
- };
3560
- //#endregion
3561
- //#region src/node/commands/agent/command.ts
3562
- const command$2 = {
3563
- name: "agent",
3564
- description: "Print guidance for connecting LLM agents to Rollipop diagnostics.",
3565
- helpText: getAgentGuide(),
3566
- action: action$2
3567
- };
3568
- //#endregion
3569
- //#region src/node/constants.ts
3570
- const UNSUPPORTED_OPTION_DESCRIPTION = "This option is not supported by Rollipop.";
3571
- //#endregion
3572
- //#region src/config/defaults.ts
3573
- async function getDefaultConfig(projectRoot, mode) {
3574
- let reactNativePath;
3575
- try {
3576
- reactNativePath = process.env.ROLLIPOP_REACT_NATIVE_PATH ?? resolvePackagePath(projectRoot, "react-native");
3577
- } catch {
3578
- throw new Error(`Could not resolve 'react-native' package path. Please check your project path.`);
3579
- }
3580
- return {
3581
- root: projectRoot,
3582
- mode: mode ?? "development",
3583
- entry: "index.js",
3584
- resolver: {
3585
- sourceExtensions: DEFAULT_SOURCE_EXTENSIONS,
3586
- assetExtensions: DEFAULT_ASSET_EXTENSIONS,
3587
- mainFields: DEFAULT_RESOLVER_MAIN_FIELDS,
3588
- aliasFields: DEFAULT_RESOLVER_ALIAS_FIELDS,
3589
- conditionNames: DEFAULT_RESOLVER_CONDITION_NAMES,
3590
- preferNativePlatform: true,
3591
- symlinks: true
3592
- },
3593
- transformer: { flow: { filter: {
3594
- id: /\.jsx?$/,
3595
- code: /@flow/
3596
- } } },
3597
- serializer: {
3598
- prelude: [getInitializeCorePath(projectRoot)],
3599
- polyfills: await Promise.all(getPolyfillScriptPaths(reactNativePath).map(async (path) => {
3600
- return {
3601
- type: "iife",
3602
- code: (await stripFlowTypes(path, fs.readFileSync(path, "utf-8"))).code
3603
- };
3604
- }))
3605
- },
3606
- watcher: {
3607
- skipWrite: true,
3608
- useDebounce: true,
3609
- debounceDuration: 50
3610
- },
3611
- optimization: { treeshake: true },
3612
- reactNative: {
3613
- reactNativePath,
3614
- codegen: { filter: { code: /\bcodegenNativeComponent</ } },
3615
- assetRegistryPath: DEFAULT_ASSET_REGISTRY_PATH,
3616
- hmrClientPath: DEFAULT_HMR_CLIENT_PATH,
3617
- globalIdentifiers: DEFAULT_REACT_NATIVE_GLOBAL_IDENTIFIERS
3618
- },
3619
- devMode: { hmr: true },
3620
- reporter: new ClientLogReporter(),
3621
- terminal: { status: (() => {
3622
- if (isDebugEnabled()) return "compat";
3623
- if (process.stderr.isTTY) return "progress";
3624
- return "compat";
3625
- })() },
3626
- envDir: projectRoot,
3627
- envFile: DEFAULT_ENV_FILE,
3628
- envPrefix: DEFAULT_ENV_PREFIX,
3629
- runtimeTarget: DEFAULT_RUNTIME_TARGET,
3630
- experimental: { nativeTransformPipeline: false }
3631
- };
3632
- }
3633
- //#endregion
3634
- //#region src/config/merge-config.ts
3635
- function mergeConfig(baseConfig, ...overrideConfigs) {
3636
- let mergedConfig = baseConfig;
3637
- for (const overrideConfig of overrideConfigs) mergedConfig = mergeWith(mergedConfig, overrideConfig, (target, source, key) => {
3638
- if ([
3639
- "sourceExtensions",
3640
- "assetExtensions",
3641
- "polyfills",
3642
- "prelude",
3643
- "plugins"
3644
- ].includes(key)) return Array.from(new Set([...target ?? [], ...source ?? []]));
3645
- if (key === "reporter") return source ?? target;
3646
- if (key === "dangerously_overrideRolldownOptions") return composeOverrideRolldownOptions(target, source);
3647
- });
3648
- return mergedConfig;
3649
- }
3650
- //#endregion
3651
- //#region src/config/load-config.ts
3652
- const CONFIG_FILE_NAME = "rollipop";
3653
- async function loadConfig(options = {}) {
3654
- const { cwd = process.cwd(), configFile, mode, context = {} } = options;
3655
- const defaultConfig = await getDefaultConfig(cwd, mode);
3656
- const commonOptions = {
3657
- context: {
3658
- ...context,
3659
- defaultConfig
3660
- },
3661
- rcFile: false
3662
- };
3663
- const { config: userConfig } = await c12.loadConfig(configFile ? {
3664
- configFile: path.resolve(cwd, configFile),
3665
- configFileRequired: true
3666
- } : {
3667
- cwd,
3668
- defaultConfig,
3669
- name: CONFIG_FILE_NAME,
3670
- ...commonOptions
3671
- });
3672
- const plugins = await flattenPluginOption(userConfig.plugins);
3673
- const resolvedConfig = {
3674
- ...await resolvePluginConfig(mergeConfig(defaultConfig, {
3675
- ...userConfig,
3676
- plugins
3677
- }), plugins),
3678
- plugins
3679
- };
3680
- await invokeConfigResolved(resolvedConfig, plugins);
3681
- return resolvedConfig;
3682
- }
3683
- async function flattenPluginOption(pluginOption) {
3684
- const awaitedPluginOption = await pluginOption;
3685
- if (Array.isArray(awaitedPluginOption)) return (await Promise.all(awaitedPluginOption.map(flattenPluginOption))).flat();
3686
- if (awaitedPluginOption == null || awaitedPluginOption === false) return [];
3687
- return [awaitedPluginOption];
3688
- }
3689
- async function resolvePluginConfig(baseConfig, plugins) {
3690
- let mergedConfig = omit(baseConfig, ["plugins"]);
3691
- for (const plugin of plugins) {
3692
- const context = createPluginContext(plugin.name);
3693
- const overrideBefore = mergedConfig.dangerously_overrideRolldownOptions;
3694
- if (typeof plugin.config === "function") {
3695
- const config = await plugin.config.call(context, mergedConfig);
3696
- if (config != null) mergedConfig = mergeConfig(mergedConfig, config);
3697
- } else if (typeof plugin.config === "object") mergedConfig = mergeConfig(mergedConfig, plugin.config);
3698
- const overrideAfter = mergedConfig.dangerously_overrideRolldownOptions;
3699
- if (overrideAfter != null && overrideAfter !== overrideBefore) context.debug({ message: `set 'dangerously_overrideRolldownOptions'` });
3700
- }
3701
- return mergedConfig;
3702
- }
3703
- async function invokeConfigResolved(config, plugins) {
3704
- await Promise.all(plugins.map((plugin) => {
3705
- const context = createPluginContext(plugin.name);
3706
- return plugin.configResolved?.call(context, config);
3707
- }));
3708
- }
3709
- //#endregion
3710
- //#region src/utils/run-build.ts
3711
- async function runBuild(config, options) {
3712
- return await new Bundler(config).build(options);
3713
- }
3714
- //#endregion
3715
- //#region src/utils/run-server.ts
3716
- async function runServer(config, options) {
3717
- const { port, host } = options;
3718
- const devServer = await createDevServer(config, options);
3719
- await devServer.instance.listen({
3720
- port,
3721
- host
3722
- });
3723
- return devServer;
3724
- }
3725
- //#endregion
3726
- //#region src/node/commands/start/action.ts
3727
- const action$1 = async function(options) {
3728
- const cwd = process.cwd();
3729
- const config = await loadConfig({
3730
- cwd,
3731
- mode: "development",
3732
- configFile: options.config,
3733
- context: { command: "start" }
3734
- });
3735
- if (options.resetCache) {
3736
- resetCache(cwd);
3737
- logger$2.info("The transform cache was reset");
3738
- }
3739
- if (options.clientLogs === false) config.reporter = { update: noop };
3740
- const devServer = await runServer(config, {
3741
- buildOptions: { cache: options.cache },
3742
- port: options.port,
3743
- host: options.host,
3744
- https: options.https,
3745
- key: options.key,
3746
- cert: options.cert
3747
- });
3748
- if (options.interactive) setupInteractiveMode({
3749
- devServer,
3750
- extraCommands: config.terminal?.extraCommands
3751
- });
3752
- };
3753
- //#endregion
3754
- //#region src/node/commands/start/command.ts
3755
- const command$1 = {
3756
- name: "start",
3757
- description: "Start the React Native development server.",
3758
- action: action$1,
3759
- options: [
3760
- {
3761
- name: "--config <string>",
3762
- description: "Path to the CLI configuration file",
3763
- parse: resolvePath
3764
- },
3765
- {
3766
- name: "--host <string>",
3767
- description: "Host to start the development server on",
3768
- default: DEFAULT_HOST
3769
- },
3770
- {
3771
- name: "--port <number>",
3772
- description: "Port to start the development server on",
3773
- default: DEFAULT_PORT,
3774
- parse: Number
3775
- },
3776
- {
3777
- name: "--reset-cache, --resetCache",
3778
- description: "Removes cached files"
3779
- },
3780
- {
3781
- name: "--https",
3782
- description: "Enables https connections to the server"
3783
- },
3784
- {
3785
- name: "--key <path>",
3786
- description: "Path to custom SSL key"
3787
- },
3788
- {
3789
- name: "--cert <path>",
3790
- description: "Path to custom SSL cert"
3791
- },
3792
- {
3793
- name: "--no-interactive",
3794
- description: "Disables interactive mode"
3795
- },
3796
- {
3797
- name: "--client-logs",
3798
- description: "[Deprecated] Enable plain text JavaScript log streaming for all connected apps. This feature is deprecated and will be removed in future.",
3799
- default: false
3800
- },
3801
- {
3802
- name: "--cache [boolean]",
3803
- description: "If false, the bundler will not load or store any cache",
3804
- parse: parseBoolean
3805
- },
3806
- {
3807
- name: "--projectRoot <path>",
3808
- description: UNSUPPORTED_OPTION_DESCRIPTION
3809
- },
3810
- {
3811
- name: "--watchFolders <list>",
3812
- description: UNSUPPORTED_OPTION_DESCRIPTION
3813
- },
3814
- {
3815
- name: "--assetPlugins <list>",
3816
- description: UNSUPPORTED_OPTION_DESCRIPTION
3817
- },
3818
- {
3819
- name: "--sourceExts <list>",
3820
- description: UNSUPPORTED_OPTION_DESCRIPTION
3821
- },
3822
- {
3823
- name: "--max-workers <number>",
3824
- description: UNSUPPORTED_OPTION_DESCRIPTION
3825
- },
3826
- {
3827
- name: "--transformer <string>",
3828
- description: UNSUPPORTED_OPTION_DESCRIPTION
3829
- },
3830
- {
3831
- name: "--custom-log-reporter-path, --customLogReporterPath <string>",
3832
- description: UNSUPPORTED_OPTION_DESCRIPTION
3833
- }
3834
- ]
3835
- };
3836
- //#endregion
3837
- //#region src/node/commands/bundle/action.ts
3838
- const action = async function(options) {
3839
- if (!this.platforms.includes(options.platform)) throw new Error(`Unrecognized platform: ${options.platform}`);
3840
- const cwd = process.cwd();
3841
- const config = await loadConfig({
3842
- cwd,
3843
- mode: "production",
3844
- configFile: options.config,
3845
- context: { command: "bundle" }
3846
- });
3847
- if (options.resetCache) {
3848
- resetCache(cwd);
3849
- logger$2.info("The transform cache was reset");
3850
- }
3851
- if (options.entryFile) config.entry = options.entryFile;
3852
- await runBuild(config, {
3853
- platform: options.platform,
3854
- dev: options.dev,
3855
- minify: options.minify,
3856
- cache: options.cache,
3857
- outfile: options.bundleOutput,
3858
- sourcemapOutfile: options.sourcemapOutput,
3859
- assetsDir: options.assetsDest
3860
- });
3861
- };
3862
- //#endregion
3863
- //#region src/node/commands/bundle/command.ts
3864
- const command = {
3865
- name: "bundle",
3866
- description: "Build the bundle for the provided JavaScript entry file.",
3867
- action,
3868
- options: [
3869
- {
3870
- name: "--config <string>",
3871
- description: "Path to the CLI configuration file",
3872
- parse: resolvePath
3873
- },
3874
- {
3875
- name: "--entry-file <path>",
3876
- description: "Path to the root JS file, either absolute or relative to JS root"
3877
- },
3878
- {
3879
- name: "--platform <string>",
3880
- description: "Either \"ios\" or \"android\"",
3881
- default: "ios"
3882
- },
3883
- {
3884
- name: "--dev [boolean]",
3885
- description: "If false, warnings are disabled and the bundle is minified",
3886
- parse: parseBoolean
3887
- },
3888
- {
3889
- name: "--minify [boolean]",
3890
- description: "Allows overriding whether bundle is minified. This defaults to false if dev is true, and true if dev is false. Disabling minification can be useful for speeding up production builds for testing purposes.",
3891
- parse: parseBoolean
3892
- },
3893
- {
3894
- name: "--cache [boolean]",
3895
- description: "If false, the bundler will not load or store any cache",
3896
- parse: parseBoolean
3897
- },
3898
- {
3899
- name: "--bundle-output <string>",
3900
- description: "File name where to store the resulting bundle, ex. /tmp/groups.bundle",
3901
- required: true
3902
- },
3903
- {
3904
- name: "--sourcemap-output <string>",
3905
- description: "File name where to store the sourcemap file for resulting bundle, ex. /tmp/groups.map"
3906
- },
3907
- {
3908
- name: "--sourcemap-sources-root <string>",
3909
- description: `Path to make sourcemap's sources entries relative to, ex. /root/dir`
3910
- },
3911
- {
3912
- name: "--sourcemap-use-absolute-path",
3913
- description: "Report SourceMapURL using its full path"
3914
- },
3915
- {
3916
- name: "--assets-dest <string>",
3917
- description: "Directory name where to store assets referenced in the bundle"
3918
- },
3919
- {
3920
- name: "--reset-cache",
3921
- description: "Removes cached files",
3922
- default: false
3923
- },
3924
- {
3925
- name: "--transformer <string>",
3926
- description: UNSUPPORTED_OPTION_DESCRIPTION
3927
- },
3928
- {
3929
- name: "--bundle-encoding <string>",
3930
- description: UNSUPPORTED_OPTION_DESCRIPTION
3931
- },
3932
- {
3933
- name: "--max-workers <number>",
3934
- description: UNSUPPORTED_OPTION_DESCRIPTION
3935
- },
3936
- {
3937
- name: "--unstable-transform-profile <string>",
3938
- description: UNSUPPORTED_OPTION_DESCRIPTION
3939
- },
3940
- {
3941
- name: "--asset-catalog-dest [string]",
3942
- description: UNSUPPORTED_OPTION_DESCRIPTION
3943
- },
3944
- {
3945
- name: "--read-global-cache",
3946
- description: UNSUPPORTED_OPTION_DESCRIPTION
3947
- },
3948
- {
3949
- name: "--resolver-option <string...>",
3950
- description: UNSUPPORTED_OPTION_DESCRIPTION
3951
- }
3952
- ]
3953
- };
3954
- //#endregion
1
+ import { createReactNativeCliCommand } from "./node/cli-utils.js";
2
+ import { command } from "./node/commands/agent/command.js";
3
+ import "./node/commands/agent/index.js";
4
+ import { command as command$1 } from "./node/commands/start/command.js";
5
+ import "./node/commands/start/index.js";
6
+ import { command as command$2 } from "./node/commands/bundle/command.js";
7
+ import "./node/commands/bundle/index.js";
3955
8
  //#region src/commands.ts
3956
9
  const commands = [
3957
10
  createReactNativeCliCommand(command$1),
3958
- createReactNativeCliCommand(command),
3959
- createReactNativeCliCommand(command$2)
11
+ createReactNativeCliCommand(command$2),
12
+ createReactNativeCliCommand(command)
3960
13
  ];
3961
14
  //#endregion
3962
- export { commands as default };
15
+ export { commands as default, commands as "module.exports" };