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