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