@rollipop/core 0.0.0 → 0.1.0-alpha.0
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 +12 -0
- package/README.md +1 -1
- package/dist/chunk-BYW8Mqxw.js +21 -0
- package/dist/hmr-client.cjs +212 -0
- package/dist/hmr-runtime.js +158 -0
- package/dist/hmr-shims.js +10 -0
- package/dist/index.cjs +1700 -0
- package/dist/index.d.cts +419 -0
- package/dist/index.d.ts +417 -0
- package/dist/index.js +1619 -0
- package/package.json +78 -2
- package/.editorconfig +0 -10
- package/.gitattributes +0 -4
package/dist/index.js
ADDED
|
@@ -0,0 +1,1619 @@
|
|
|
1
|
+
import { n as __require, t as __export } from "./chunk-BYW8Mqxw.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { DEV_SERVER_ASSET_PATH, FileStorage, Logger, Logo, chalk, getCachePath } from "@rollipop/common";
|
|
4
|
+
import { invariant, isNotNil, merge, mergeWith, pick, range, throttle } from "es-toolkit";
|
|
5
|
+
import * as rolldown from "rolldown";
|
|
6
|
+
import * as rolldownExperimental from "rolldown/experimental";
|
|
7
|
+
import { dev, transformSync } from "rolldown/experimental";
|
|
8
|
+
import crypto from "node:crypto";
|
|
9
|
+
import { xxh32 } from "@node-rs/xxhash";
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import dedent from "dedent";
|
|
12
|
+
import wrapAnsi from "wrap-ansi";
|
|
13
|
+
import path$1 from "path";
|
|
14
|
+
import * as babel from "@babel/core";
|
|
15
|
+
import { exactRegex } from "@rolldown/pluginutils";
|
|
16
|
+
import { generate } from "@babel/generator";
|
|
17
|
+
import * as swc from "@swc/core";
|
|
18
|
+
import flowRemoveTypes from "flow-remove-types";
|
|
19
|
+
import * as hermesParser from "hermes-parser";
|
|
20
|
+
import { imageSize } from "image-size";
|
|
21
|
+
import { transform } from "@svgr/core";
|
|
22
|
+
import * as c12 from "c12";
|
|
23
|
+
|
|
24
|
+
//#region src/constants.ts
|
|
25
|
+
const ROLLIPOP_VERSION = "0.1.0-alpha.0";
|
|
26
|
+
const GLOBAL_IDENTIFIER = "__ROLLIPOP_GLOBAL__";
|
|
27
|
+
/**
|
|
28
|
+
* Unlike the Metro bundler configuration, this prioritizes resolving module(ESM) fields first.
|
|
29
|
+
*
|
|
30
|
+
* @see {@link https://github.com/facebook/metro/blob/0.81.x/docs/Configuration.md#resolvermainfields}
|
|
31
|
+
*/
|
|
32
|
+
const DEFAULT_RESOLVER_MAIN_FIELDS = [
|
|
33
|
+
"react-native",
|
|
34
|
+
"module",
|
|
35
|
+
"main"
|
|
36
|
+
];
|
|
37
|
+
const DEFAULT_RESOLVER_CONDITION_NAMES = [
|
|
38
|
+
"react-native",
|
|
39
|
+
"import",
|
|
40
|
+
"require"
|
|
41
|
+
];
|
|
42
|
+
/**
|
|
43
|
+
* Unlike the Metro bundler configuration, this prioritizes resolving TypeScript and ESM first.
|
|
44
|
+
*
|
|
45
|
+
* @see {@link https://github.com/facebook/metro/blob/0.81.x/packages/metro-config/src/defaults/defaults.js}
|
|
46
|
+
* @see {@link https://github.com/facebook/metro/blob/0.81.x/packages/metro-file-map/src/workerExclusionList.js}
|
|
47
|
+
*/
|
|
48
|
+
const DEFAULT_SOURCE_EXTENSIONS = [
|
|
49
|
+
"ts",
|
|
50
|
+
"tsx",
|
|
51
|
+
"js",
|
|
52
|
+
"jsx",
|
|
53
|
+
"mjs",
|
|
54
|
+
"cjs",
|
|
55
|
+
"json"
|
|
56
|
+
];
|
|
57
|
+
const DEFAULT_ASSET_EXTENSIONS = [
|
|
58
|
+
"bmp",
|
|
59
|
+
"gif",
|
|
60
|
+
"jpg",
|
|
61
|
+
"jpeg",
|
|
62
|
+
"png",
|
|
63
|
+
"psd",
|
|
64
|
+
"svg",
|
|
65
|
+
"webp",
|
|
66
|
+
"xml",
|
|
67
|
+
"m4v",
|
|
68
|
+
"mov",
|
|
69
|
+
"mp4",
|
|
70
|
+
"mpeg",
|
|
71
|
+
"mpg",
|
|
72
|
+
"webm",
|
|
73
|
+
"aac",
|
|
74
|
+
"aiff",
|
|
75
|
+
"caf",
|
|
76
|
+
"m4a",
|
|
77
|
+
"mp3",
|
|
78
|
+
"wav",
|
|
79
|
+
"html",
|
|
80
|
+
"pdf",
|
|
81
|
+
"yaml",
|
|
82
|
+
"yml",
|
|
83
|
+
"otf",
|
|
84
|
+
"ttf",
|
|
85
|
+
"zip"
|
|
86
|
+
];
|
|
87
|
+
const DEFAULT_ASSET_REGISTRY_PATH = "react-native/Libraries/Image/AssetRegistry.js";
|
|
88
|
+
const DEFAULT_HMR_CLIENT_PATH = "react-native/Libraries/Utilities/HMRClient.js";
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/utils/hash.ts
|
|
92
|
+
function xxhash(data) {
|
|
93
|
+
return xxh32(data).toString(16);
|
|
94
|
+
}
|
|
95
|
+
function md5(data) {
|
|
96
|
+
return crypto.createHash("md5").update(data).digest("hex");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/utils/serialize.ts
|
|
101
|
+
function serialize(value) {
|
|
102
|
+
return JSON.stringify(value, (_, value$1) => {
|
|
103
|
+
if (typeof value$1 === "function") return value$1.toString();
|
|
104
|
+
if (value$1 instanceof RegExp) return value$1.toString();
|
|
105
|
+
return value$1;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/utils/id.ts
|
|
111
|
+
function createId(config, buildOptions) {
|
|
112
|
+
return md5(serialize([
|
|
113
|
+
ROLLIPOP_VERSION,
|
|
114
|
+
pick(buildOptions, ["platform", "dev"]),
|
|
115
|
+
config.resolver,
|
|
116
|
+
config.transformer,
|
|
117
|
+
config.plugins,
|
|
118
|
+
config.serializer.prelude
|
|
119
|
+
]));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/logger.ts
|
|
124
|
+
const logger = new Logger("bundler");
|
|
125
|
+
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/core/cache/file-system-cache.ts
|
|
128
|
+
var FileSystemCache = class {
|
|
129
|
+
constructor(cacheDirectory) {
|
|
130
|
+
this.cacheDirectory = cacheDirectory;
|
|
131
|
+
this.ensureCacheDirectory();
|
|
132
|
+
logger.debug("cache directory:", cacheDirectory);
|
|
133
|
+
}
|
|
134
|
+
ensureCacheDirectory() {
|
|
135
|
+
if (!fs.existsSync(this.cacheDirectory)) fs.mkdirSync(this.cacheDirectory, { recursive: true });
|
|
136
|
+
}
|
|
137
|
+
get(key) {
|
|
138
|
+
try {
|
|
139
|
+
return fs.readFileSync(path.join(this.cacheDirectory, key), "utf-8");
|
|
140
|
+
} catch {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
set(key, value) {
|
|
145
|
+
try {
|
|
146
|
+
fs.writeFileSync(path.join(this.cacheDirectory, key), value);
|
|
147
|
+
} catch {
|
|
148
|
+
logger.error("Failed to write cache file", key);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
clear() {
|
|
152
|
+
fs.rmSync(this.cacheDirectory, {
|
|
153
|
+
recursive: true,
|
|
154
|
+
force: true
|
|
155
|
+
});
|
|
156
|
+
this.ensureCacheDirectory();
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region ../common/src/constants.ts
|
|
162
|
+
const DEBUG_KEY = "rollipop";
|
|
163
|
+
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region ../common/src/debug.ts
|
|
166
|
+
const TRUTHY_VALUES = [
|
|
167
|
+
"yes",
|
|
168
|
+
"on",
|
|
169
|
+
"true",
|
|
170
|
+
"enabled"
|
|
171
|
+
];
|
|
172
|
+
const FALSY_VALUES = [
|
|
173
|
+
"no",
|
|
174
|
+
"off",
|
|
175
|
+
"false",
|
|
176
|
+
"disabled"
|
|
177
|
+
];
|
|
178
|
+
function parseDebugKeys() {
|
|
179
|
+
return Object.keys(process.env).filter((key) => /^debug_/i.test(key)).reduce((acc, key) => {
|
|
180
|
+
const prop = key.slice(6).toLowerCase().replace(/_([a-z])/g, (_, key$1) => key$1.toUpperCase());
|
|
181
|
+
let value = process.env[key];
|
|
182
|
+
const lowerCase = typeof value === "string" ? value.toLowerCase() : value.toString();
|
|
183
|
+
if (TRUTHY_VALUES.includes(lowerCase)) value = true;
|
|
184
|
+
else if (FALSY_VALUES.includes(lowerCase)) value = false;
|
|
185
|
+
else value = Boolean(Number(value));
|
|
186
|
+
acc[prop] = value;
|
|
187
|
+
return acc;
|
|
188
|
+
}, {});
|
|
189
|
+
}
|
|
190
|
+
let debugKeys = null;
|
|
191
|
+
function isDebugEnabled() {
|
|
192
|
+
if (debugKeys == null) debugKeys = parseDebugKeys();
|
|
193
|
+
return debugKeys[DEBUG_KEY] ?? false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/utils/string.ts
|
|
198
|
+
function indent(text, indent$1, space = " ") {
|
|
199
|
+
return text.replace(/^/gm, space.repeat(indent$1));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/common/code.ts
|
|
204
|
+
function asLiteral(value) {
|
|
205
|
+
return JSON.stringify(value);
|
|
206
|
+
}
|
|
207
|
+
function asIdentifier(name) {
|
|
208
|
+
return name;
|
|
209
|
+
}
|
|
210
|
+
function nodeEnvironment(dev$1) {
|
|
211
|
+
return dev$1 ? "development" : "production";
|
|
212
|
+
}
|
|
213
|
+
function iife(body, path$2 = "<unknown>") {
|
|
214
|
+
const bodyPlaceholder = "__BODY__";
|
|
215
|
+
return dedent`
|
|
216
|
+
// ${path$2}
|
|
217
|
+
(function (global) {
|
|
218
|
+
${bodyPlaceholder}
|
|
219
|
+
})(${GLOBAL_IDENTIFIER});
|
|
220
|
+
`.replace(bodyPlaceholder, indent(body, 1));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/utils/storage.ts
|
|
225
|
+
function getBuildTotalModules(storage, id) {
|
|
226
|
+
return storage.get().build[id]?.totalModules ?? 0;
|
|
227
|
+
}
|
|
228
|
+
function setBuildTotalModules(storage, id, totalModules) {
|
|
229
|
+
storage.set({ build: { [id]: { totalModules } } });
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
//#endregion
|
|
233
|
+
//#region src/utils/terminal.ts
|
|
234
|
+
/**
|
|
235
|
+
* Based on https://github.com/sindresorhus/log-update/blob/master/index.js
|
|
236
|
+
* Based on https://github.com/unjs/webpackbar
|
|
237
|
+
*/
|
|
238
|
+
function eraseLines(count) {
|
|
239
|
+
let clear = "";
|
|
240
|
+
for (let i = 0; i < count; i++) clear += `\u001B[2K` + (i < count - 1 ? `\u001B[1A` : "");
|
|
241
|
+
if (count) clear += `\u001B[G`;
|
|
242
|
+
return clear;
|
|
243
|
+
}
|
|
244
|
+
function ellipsisLeft(value, maxLength) {
|
|
245
|
+
if (value.length <= maxLength - 3) return value;
|
|
246
|
+
return `...${value.slice(value.length - maxLength - 1)}`;
|
|
247
|
+
}
|
|
248
|
+
const originalWrite = Symbol("original-write");
|
|
249
|
+
var StreamManager = class {
|
|
250
|
+
prevLineCount;
|
|
251
|
+
listening;
|
|
252
|
+
extraLines;
|
|
253
|
+
_streams;
|
|
254
|
+
constructor() {
|
|
255
|
+
this.prevLineCount = 0;
|
|
256
|
+
this.listening = false;
|
|
257
|
+
this.extraLines = "";
|
|
258
|
+
this._onData = this._onData.bind(this);
|
|
259
|
+
this._streams = [process.stdout, process.stderr];
|
|
260
|
+
}
|
|
261
|
+
render(lines) {
|
|
262
|
+
this.listen();
|
|
263
|
+
const wrappedLines = wrapAnsi(lines, this.columns, {
|
|
264
|
+
trim: false,
|
|
265
|
+
hard: true,
|
|
266
|
+
wordWrap: false
|
|
267
|
+
});
|
|
268
|
+
const data = eraseLines(this.prevLineCount) + wrappedLines + "\n" + this.extraLines;
|
|
269
|
+
this.write(data);
|
|
270
|
+
this.prevLineCount = data.split("\n").length;
|
|
271
|
+
}
|
|
272
|
+
get columns() {
|
|
273
|
+
return (process.stderr.columns || 80) - 2;
|
|
274
|
+
}
|
|
275
|
+
write(data) {
|
|
276
|
+
const stream = process.stderr;
|
|
277
|
+
if (stream.write[originalWrite]) stream.write[originalWrite].call(stream, data, "utf8");
|
|
278
|
+
else stream.write(data, "utf8");
|
|
279
|
+
}
|
|
280
|
+
clear() {
|
|
281
|
+
this.done();
|
|
282
|
+
this.write(eraseLines(this.prevLineCount));
|
|
283
|
+
}
|
|
284
|
+
done() {
|
|
285
|
+
this.stopListen();
|
|
286
|
+
this.prevLineCount = 0;
|
|
287
|
+
this.extraLines = "";
|
|
288
|
+
}
|
|
289
|
+
_onData(data) {
|
|
290
|
+
const lines = String(data).split("\n").length - 1;
|
|
291
|
+
if (lines > 0) {
|
|
292
|
+
this.prevLineCount += lines;
|
|
293
|
+
this.extraLines += data;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
listen() {
|
|
297
|
+
if (this.listening) return;
|
|
298
|
+
for (const stream of this._streams) {
|
|
299
|
+
if (stream.write[originalWrite]) continue;
|
|
300
|
+
const write = (data, ...args) => {
|
|
301
|
+
if (!stream.write[originalWrite]) return stream.write(data, ...args);
|
|
302
|
+
this._onData(data);
|
|
303
|
+
return stream.write[originalWrite].call(stream, data, ...args);
|
|
304
|
+
};
|
|
305
|
+
write[originalWrite] = stream.write;
|
|
306
|
+
stream.write = write;
|
|
307
|
+
}
|
|
308
|
+
this.listening = true;
|
|
309
|
+
}
|
|
310
|
+
stopListen() {
|
|
311
|
+
for (const stream of this._streams) if (stream.write[originalWrite]) stream.write = stream.write[originalWrite];
|
|
312
|
+
this.listening = false;
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/common/progress-bar.ts
|
|
318
|
+
const BAR_LENGTH = 25;
|
|
319
|
+
const BLOCK_CHAR = "█";
|
|
320
|
+
var ProgressBar = class {
|
|
321
|
+
columns = (process.stderr.columns || 80) - 2;
|
|
322
|
+
state = null;
|
|
323
|
+
current = 0;
|
|
324
|
+
total = 0;
|
|
325
|
+
label;
|
|
326
|
+
stale = false;
|
|
327
|
+
done = false;
|
|
328
|
+
constructor(options) {
|
|
329
|
+
this.total = options.total;
|
|
330
|
+
this.label = options.label;
|
|
331
|
+
}
|
|
332
|
+
setCurrent(current) {
|
|
333
|
+
this.current = current;
|
|
334
|
+
this.stale = true;
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
setTotal(total) {
|
|
338
|
+
this.total = total;
|
|
339
|
+
this.stale = true;
|
|
340
|
+
return this;
|
|
341
|
+
}
|
|
342
|
+
start() {
|
|
343
|
+
this.stale = true;
|
|
344
|
+
this.done = false;
|
|
345
|
+
}
|
|
346
|
+
end() {
|
|
347
|
+
this.done = true;
|
|
348
|
+
}
|
|
349
|
+
update(state) {
|
|
350
|
+
this.state = state;
|
|
351
|
+
this.stale = true;
|
|
352
|
+
return this;
|
|
353
|
+
}
|
|
354
|
+
render() {
|
|
355
|
+
const { state, label, current, total } = this;
|
|
356
|
+
const unknownTotal = total === 0;
|
|
357
|
+
const progress = unknownTotal ? 0 : current / total * 100;
|
|
358
|
+
let line1 = "";
|
|
359
|
+
let line2 = "";
|
|
360
|
+
if (state && "duration" in state) {
|
|
361
|
+
const icon = state.hasErrors ? chalk.red("✘") : chalk.green("✔");
|
|
362
|
+
const durationInSeconds = (state.duration / 1e3).toFixed(2);
|
|
363
|
+
line1 = `${icon} Build completed ${chalk.gray(label)}`;
|
|
364
|
+
line2 = chalk.grey(` Built in ${durationInSeconds}s (${current}/${total} modules)`);
|
|
365
|
+
} else {
|
|
366
|
+
const width = unknownTotal ? 0 : progress * (BAR_LENGTH / 100);
|
|
367
|
+
const bg = chalk.white(BLOCK_CHAR);
|
|
368
|
+
const fg = chalk.cyan(BLOCK_CHAR);
|
|
369
|
+
const bar = range(BAR_LENGTH).map((n) => n < width ? fg : bg).join("");
|
|
370
|
+
const progressLabel = unknownTotal ? chalk.gray("(calculating...)") : `(${progress.toFixed(2)}%)`;
|
|
371
|
+
const moduleCountLabel = unknownTotal ? `${current} modules` : `${current}/${total} modules`;
|
|
372
|
+
line1 = [
|
|
373
|
+
chalk.cyan("●"),
|
|
374
|
+
bar,
|
|
375
|
+
progressLabel,
|
|
376
|
+
chalk.gray(moduleCountLabel),
|
|
377
|
+
chalk.gray(label)
|
|
378
|
+
].join(" ");
|
|
379
|
+
line2 = state?.id ? " " + chalk.grey(ellipsisLeft(state?.id, this.columns - 10)) : "";
|
|
380
|
+
}
|
|
381
|
+
this.stale = false;
|
|
382
|
+
return `${line1}\n${line2}`;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
var ProgressBarRenderer = class ProgressBarRenderer {
|
|
386
|
+
static instance = null;
|
|
387
|
+
streamManager = new StreamManager();
|
|
388
|
+
progressBars = /* @__PURE__ */ new Map();
|
|
389
|
+
throttledRender;
|
|
390
|
+
static getInstance() {
|
|
391
|
+
if (!ProgressBarRenderer.instance) ProgressBarRenderer.instance = new ProgressBarRenderer();
|
|
392
|
+
return ProgressBarRenderer.instance;
|
|
393
|
+
}
|
|
394
|
+
constructor() {
|
|
395
|
+
this.throttledRender = throttle(this._render.bind(this), 50);
|
|
396
|
+
}
|
|
397
|
+
_render() {
|
|
398
|
+
const renderedLines = Array.from(this.progressBars.values().filter((progressBar$1) => progressBar$1.stale).map((progressBar$1) => progressBar$1.render()));
|
|
399
|
+
if (renderedLines.length > 0) this.streamManager.render(renderedLines.join("\n\n"));
|
|
400
|
+
}
|
|
401
|
+
register(key, options) {
|
|
402
|
+
const progressBar$1 = this.progressBars.get(key);
|
|
403
|
+
if (progressBar$1 == null) {
|
|
404
|
+
const newProgressBar = new ProgressBar(options);
|
|
405
|
+
this.progressBars.set(key, newProgressBar);
|
|
406
|
+
return newProgressBar;
|
|
407
|
+
}
|
|
408
|
+
return progressBar$1;
|
|
409
|
+
}
|
|
410
|
+
start() {
|
|
411
|
+
console.log();
|
|
412
|
+
this.streamManager.listen();
|
|
413
|
+
this._render();
|
|
414
|
+
}
|
|
415
|
+
render() {
|
|
416
|
+
this.throttledRender();
|
|
417
|
+
}
|
|
418
|
+
release() {
|
|
419
|
+
if (this.progressBars.values().every((progressBar$1) => progressBar$1.done)) {
|
|
420
|
+
this._render();
|
|
421
|
+
this.streamManager.done();
|
|
422
|
+
console.log();
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
clear() {
|
|
426
|
+
this.streamManager.clear();
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
//#endregion
|
|
431
|
+
//#region src/common/status-presets.ts
|
|
432
|
+
var ProgressFlags = /* @__PURE__ */ function(ProgressFlags$1) {
|
|
433
|
+
ProgressFlags$1[ProgressFlags$1["NONE"] = 0] = "NONE";
|
|
434
|
+
ProgressFlags$1[ProgressFlags$1["BUILD_IN_PROGRESS"] = 1] = "BUILD_IN_PROGRESS";
|
|
435
|
+
ProgressFlags$1[ProgressFlags$1["WATCH_CHANGE"] = 2] = "WATCH_CHANGE";
|
|
436
|
+
return ProgressFlags$1;
|
|
437
|
+
}(ProgressFlags || {});
|
|
438
|
+
function progressBar(reporter, context, label) {
|
|
439
|
+
let flags = ProgressFlags.NONE;
|
|
440
|
+
const initialTotalModules = getBuildTotalModules(context.storage, context.id);
|
|
441
|
+
const progressBarRenderer = ProgressBarRenderer.getInstance();
|
|
442
|
+
const progressBar$1 = progressBarRenderer.register(context.id, {
|
|
443
|
+
label,
|
|
444
|
+
total: initialTotalModules
|
|
445
|
+
});
|
|
446
|
+
const renderProgress = (id, totalModules, transformedModules) => {
|
|
447
|
+
if (totalModules != null) progressBar$1.setTotal(totalModules);
|
|
448
|
+
progressBar$1.setCurrent(transformedModules).update({ id });
|
|
449
|
+
progressBarRenderer.render();
|
|
450
|
+
};
|
|
451
|
+
return withReporter(reporter, {
|
|
452
|
+
initialTotalModules,
|
|
453
|
+
onStart() {
|
|
454
|
+
flags = ProgressFlags.BUILD_IN_PROGRESS;
|
|
455
|
+
progressBar$1.start();
|
|
456
|
+
progressBarRenderer.start();
|
|
457
|
+
},
|
|
458
|
+
onEnd({ error, duration, totalModules }) {
|
|
459
|
+
flags = ProgressFlags.NONE;
|
|
460
|
+
progressBar$1.setTotal(totalModules).update({
|
|
461
|
+
duration,
|
|
462
|
+
hasErrors: Boolean(error)
|
|
463
|
+
}).end();
|
|
464
|
+
progressBarRenderer.release();
|
|
465
|
+
setBuildTotalModules(context.storage, context.id, totalModules);
|
|
466
|
+
},
|
|
467
|
+
onTransform({ id, totalModules, transformedModules }) {
|
|
468
|
+
switch (true) {
|
|
469
|
+
case Boolean(flags & ProgressFlags.BUILD_IN_PROGRESS):
|
|
470
|
+
renderProgress(id, totalModules, transformedModules);
|
|
471
|
+
break;
|
|
472
|
+
case Boolean(flags & ProgressFlags.WATCH_CHANGE):
|
|
473
|
+
logger.debug("Transformed changed file", { id });
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
onWatchChange() {
|
|
478
|
+
flags = flags | ProgressFlags.WATCH_CHANGE;
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
function compat(reporter) {
|
|
483
|
+
return withReporter(reporter, {
|
|
484
|
+
onStart() {
|
|
485
|
+
logger.info("Build started...");
|
|
486
|
+
},
|
|
487
|
+
onEnd({ totalModules, duration }) {
|
|
488
|
+
const time = chalk.blue(`${duration.toFixed(2)}ms`);
|
|
489
|
+
const modules = chalk.blue(`(${totalModules} modules)`);
|
|
490
|
+
logger.info(`Build finished in ${time} ${modules}`);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
function withReporter(reporter, statusPluginOptions) {
|
|
495
|
+
return {
|
|
496
|
+
...statusPluginOptions,
|
|
497
|
+
onStart() {
|
|
498
|
+
reporter.update({ type: "bundle_build_started" });
|
|
499
|
+
statusPluginOptions.onStart?.();
|
|
500
|
+
},
|
|
501
|
+
onEnd(result) {
|
|
502
|
+
if (result.error) reporter.update({
|
|
503
|
+
type: "bundle_build_failed",
|
|
504
|
+
error: result.error
|
|
505
|
+
});
|
|
506
|
+
else reporter.update({ type: "bundle_build_done" });
|
|
507
|
+
statusPluginOptions.onEnd?.(result);
|
|
508
|
+
},
|
|
509
|
+
onTransform(result) {
|
|
510
|
+
reporter.update({
|
|
511
|
+
type: "transform",
|
|
512
|
+
...result
|
|
513
|
+
});
|
|
514
|
+
statusPluginOptions.onTransform?.(result);
|
|
515
|
+
},
|
|
516
|
+
onWatchChange(id) {
|
|
517
|
+
reporter.update({
|
|
518
|
+
type: "watch_change",
|
|
519
|
+
id
|
|
520
|
+
});
|
|
521
|
+
statusPluginOptions.onWatchChange?.(id);
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
const statusPresets = {
|
|
526
|
+
progressBar,
|
|
527
|
+
compat
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region src/internal/react-native.ts
|
|
532
|
+
function getInitializeCorePath(basePath) {
|
|
533
|
+
return __require.resolve("react-native/Libraries/Core/InitializeCore", { paths: [basePath] });
|
|
534
|
+
}
|
|
535
|
+
function getPolyfillScriptPaths(reactNativePath) {
|
|
536
|
+
const scriptPath = path.join(reactNativePath, "rn-get-polyfills");
|
|
537
|
+
return __require(scriptPath)();
|
|
538
|
+
}
|
|
539
|
+
function getGlobalVariables(dev$1, mode) {
|
|
540
|
+
return [
|
|
541
|
+
`var __BUNDLE_START_TIME__=globalThis.nativePerformanceNow?nativePerformanceNow():Date.now();`,
|
|
542
|
+
`var __DEV__=${dev$1};`,
|
|
543
|
+
`var ${GLOBAL_IDENTIFIER}=typeof globalThis!=='undefined'?globalThis:typeof global !== 'undefined'?global:typeof window!=='undefined'?window:this;`,
|
|
544
|
+
`var process=globalThis.process||{};process.env=process.env||{};process.env.NODE_ENV=process.env.NODE_ENV||"${dev$1 ? "development" : "production"}";`,
|
|
545
|
+
dev$1 && mode === "serve" ? `var $RefreshReg$ = () => {};` : null,
|
|
546
|
+
dev$1 && mode === "serve" ? `var $RefreshSig$ = () => (v) => v;` : null
|
|
547
|
+
].filter(isNotNil);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
//#endregion
|
|
551
|
+
//#region src/common/transformer.ts
|
|
552
|
+
function stripFlowSyntax(code, id) {
|
|
553
|
+
const typeRemoved = flowRemoveTypes(code, {
|
|
554
|
+
all: true,
|
|
555
|
+
removeEmptyImports: true
|
|
556
|
+
});
|
|
557
|
+
const generated = generate(hermesParser.parse(typeRemoved.toString(), {
|
|
558
|
+
flow: "all",
|
|
559
|
+
babel: true
|
|
560
|
+
}), {
|
|
561
|
+
sourceMaps: true,
|
|
562
|
+
sourceFileName: path.basename(id)
|
|
563
|
+
});
|
|
564
|
+
return {
|
|
565
|
+
code: generated.code,
|
|
566
|
+
map: generated.map
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
function blockScoping(code, id, dev$1, UNSTABLE_enableSourceMap = false) {
|
|
570
|
+
return swc.transformSync(code, {
|
|
571
|
+
filename: path.basename(id),
|
|
572
|
+
configFile: false,
|
|
573
|
+
swcrc: false,
|
|
574
|
+
sourceMaps: UNSTABLE_enableSourceMap,
|
|
575
|
+
jsc: {
|
|
576
|
+
target: "es5",
|
|
577
|
+
parser: {
|
|
578
|
+
syntax: "typescript",
|
|
579
|
+
tsx: true
|
|
580
|
+
},
|
|
581
|
+
keepClassNames: true,
|
|
582
|
+
loose: false,
|
|
583
|
+
transform: { react: {
|
|
584
|
+
runtime: "automatic",
|
|
585
|
+
development: dev$1
|
|
586
|
+
} },
|
|
587
|
+
assumptions: {
|
|
588
|
+
setPublicClassFields: true,
|
|
589
|
+
privateFieldsAsProperties: true
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
isModule: true
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
//#endregion
|
|
597
|
+
//#region src/core/assets.ts
|
|
598
|
+
/**
|
|
599
|
+
* **NOTE**: Type definitions are ported from `metro` implementation.
|
|
600
|
+
*
|
|
601
|
+
* @see https://github.com/facebook/metro/blob/0.81.x/packages/metro/src/Assets.js
|
|
602
|
+
*/
|
|
603
|
+
var assets_exports = /* @__PURE__ */ __export({
|
|
604
|
+
copyAssetsToDestination: () => copyAssetsToDestination,
|
|
605
|
+
getAssetPriority: () => getAssetPriority,
|
|
606
|
+
getSuffixedPath: () => getSuffixedPath,
|
|
607
|
+
platformSuffixPattern: () => platformSuffixPattern,
|
|
608
|
+
resolveAssetPath: () => resolveAssetPath,
|
|
609
|
+
resolveScaledAssets: () => resolveScaledAssets,
|
|
610
|
+
stripSuffix: () => stripSuffix
|
|
611
|
+
});
|
|
612
|
+
const SCALE_PATTERN = "@(\\d+\\.?\\d*)x";
|
|
613
|
+
/**
|
|
614
|
+
* key: platform,
|
|
615
|
+
* value: allowed scales
|
|
616
|
+
*
|
|
617
|
+
* @see https://github.com/facebook/react-native/blob/0.83-stable/packages/community-cli-plugin/src/commands/bundle/filterPlatformAssetScales.js#L11
|
|
618
|
+
*/
|
|
619
|
+
const ALLOW_SCALES = { ios: [
|
|
620
|
+
1,
|
|
621
|
+
2,
|
|
622
|
+
3
|
|
623
|
+
] };
|
|
624
|
+
/**
|
|
625
|
+
* @see https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp
|
|
626
|
+
*/
|
|
627
|
+
const ANDROID_ASSET_QUALIFIER = {
|
|
628
|
+
.75: "ldpi",
|
|
629
|
+
1: "mdpi",
|
|
630
|
+
1.5: "hdpi",
|
|
631
|
+
2: "xhdpi",
|
|
632
|
+
3: "xxhdpi",
|
|
633
|
+
4: "xxxhdpi"
|
|
634
|
+
};
|
|
635
|
+
async function resolveScaledAssets(options) {
|
|
636
|
+
const { projectRoot, assetPath, platform, preferNativePlatform } = options;
|
|
637
|
+
const context = {
|
|
638
|
+
platform,
|
|
639
|
+
preferNativePlatform
|
|
640
|
+
};
|
|
641
|
+
const extension = path.extname(assetPath);
|
|
642
|
+
const relativePath = path.relative(projectRoot, assetPath);
|
|
643
|
+
const dirname = path.dirname(assetPath);
|
|
644
|
+
const files = fs.readdirSync(dirname);
|
|
645
|
+
const stripedBasename = stripSuffix(assetPath, context);
|
|
646
|
+
const suffixPattern = platformSuffixPattern(context);
|
|
647
|
+
const assetRegExp = /* @__PURE__ */ new RegExp(`${stripedBasename}(${SCALE_PATTERN})?(?:${suffixPattern})?${extension}$`);
|
|
648
|
+
const scaledAssets = {};
|
|
649
|
+
for (const file of files.sort((a, b) => getAssetPriority(b, context) - getAssetPriority(a, context))) {
|
|
650
|
+
const match = assetRegExp.exec(file);
|
|
651
|
+
if (match) {
|
|
652
|
+
const [, , scale = "1"] = match;
|
|
653
|
+
if (scaledAssets[scale]) continue;
|
|
654
|
+
scaledAssets[scale] = file;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (!(Object.keys(scaledAssets).length && scaledAssets[1])) throw new Error(`cannot resolve base asset of ${assetPath}`);
|
|
658
|
+
const imageData = fs.readFileSync(assetPath);
|
|
659
|
+
const dimensions = imageSize(imageData);
|
|
660
|
+
const filteredScaledAssets = Object.entries(scaledAssets).map(([scale, file]) => ({
|
|
661
|
+
scale: parseFloat(scale),
|
|
662
|
+
file
|
|
663
|
+
})).filter(({ scale }) => ALLOW_SCALES[platform]?.includes(scale) ?? true).reduce((acc, { scale, file }) => {
|
|
664
|
+
acc.files.push(file);
|
|
665
|
+
acc.scales.push(scale);
|
|
666
|
+
return acc;
|
|
667
|
+
}, {
|
|
668
|
+
scales: [],
|
|
669
|
+
files: []
|
|
670
|
+
});
|
|
671
|
+
return {
|
|
672
|
+
__packager_asset: true,
|
|
673
|
+
id: assetPath,
|
|
674
|
+
name: stripedBasename.replace(extension, ""),
|
|
675
|
+
type: extension.substring(1),
|
|
676
|
+
width: dimensions.width,
|
|
677
|
+
height: dimensions.height,
|
|
678
|
+
files: filteredScaledAssets.files,
|
|
679
|
+
scales: filteredScaledAssets.scales,
|
|
680
|
+
fileSystemLocation: path.dirname(assetPath),
|
|
681
|
+
httpServerLocation: path.join(DEV_SERVER_ASSET_PATH, path.dirname(relativePath)),
|
|
682
|
+
hash: md5(imageData)
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
function platformSuffixPattern(context) {
|
|
686
|
+
return [context.platform, context.preferNativePlatform ? "native" : null].filter(isNotNil).map((platform) => `.${platform}`).join("|");
|
|
687
|
+
}
|
|
688
|
+
function stripSuffix(assetPath, context) {
|
|
689
|
+
const basename = path.basename(assetPath);
|
|
690
|
+
const extension = path.extname(assetPath);
|
|
691
|
+
const suffixPattern = platformSuffixPattern(context);
|
|
692
|
+
return basename.replace(/* @__PURE__ */ new RegExp(`(${SCALE_PATTERN})?(?:${suffixPattern})?${extension}$`), "");
|
|
693
|
+
}
|
|
694
|
+
function getAssetPriority(assetPath, context) {
|
|
695
|
+
const suffixPattern = platformSuffixPattern(context);
|
|
696
|
+
if ((/* @__PURE__ */ new RegExp(`${SCALE_PATTERN}(?:${suffixPattern})`)).test(assetPath)) return 3;
|
|
697
|
+
else if ((/* @__PURE__ */ new RegExp(`(?:${suffixPattern})`)).test(assetPath)) return 2;
|
|
698
|
+
else if ((/* @__PURE__ */ new RegExp(`${SCALE_PATTERN}`)).test(assetPath)) return 1;
|
|
699
|
+
return 0;
|
|
700
|
+
}
|
|
701
|
+
function addSuffix(assetPath, context, options) {
|
|
702
|
+
const extension = path.extname(assetPath);
|
|
703
|
+
return stripSuffix(assetPath, context).concat(options?.scale ? `@${options.scale}x` : "").concat(options?.platform ? `.${options.platform}${extension}` : extension);
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* add suffix to asset path
|
|
707
|
+
*
|
|
708
|
+
* ```js
|
|
709
|
+
* // assetPath input
|
|
710
|
+
* '/path/to/assets/image.png'
|
|
711
|
+
*
|
|
712
|
+
* // `platform` suffixed
|
|
713
|
+
* '/path/to/assets/image.android.png'
|
|
714
|
+
*
|
|
715
|
+
* // `scale` suffixed
|
|
716
|
+
* '/path/to/assets/image@1x.png'
|
|
717
|
+
*
|
|
718
|
+
* // both `platform` and `scale` suffixed
|
|
719
|
+
* '/path/to/assets/image@1x.android.png'
|
|
720
|
+
* ```
|
|
721
|
+
*/
|
|
722
|
+
function getSuffixedPath(assetPath, context, options) {
|
|
723
|
+
const suffixedBasename = addSuffix(assetPath, context, {
|
|
724
|
+
scale: options?.scale,
|
|
725
|
+
platform: options?.platform
|
|
726
|
+
});
|
|
727
|
+
const dirname = path.dirname(assetPath);
|
|
728
|
+
return path.join(dirname, suffixedBasename);
|
|
729
|
+
}
|
|
730
|
+
function resolveAssetPath(assetPath, context, scale) {
|
|
731
|
+
const suffixedPaths = [
|
|
732
|
+
getSuffixedPath(assetPath, context, {
|
|
733
|
+
scale,
|
|
734
|
+
platform: context.platform
|
|
735
|
+
}),
|
|
736
|
+
context.preferNativePlatform ? getSuffixedPath(assetPath, context, {
|
|
737
|
+
scale,
|
|
738
|
+
platform: "native"
|
|
739
|
+
}) : null,
|
|
740
|
+
getSuffixedPath(assetPath, context, { scale })
|
|
741
|
+
].filter(isNotNil);
|
|
742
|
+
/**
|
|
743
|
+
* When scale is 1, filename can be suffixed or non-suffixed(`image.png`).
|
|
744
|
+
*
|
|
745
|
+
* - Suffixed
|
|
746
|
+
* - `filename.<platform>@<scale>x.ext`
|
|
747
|
+
* - `filename.<platform>.ext`
|
|
748
|
+
* - `filename@<scale>x.ext`
|
|
749
|
+
* - Non suffixed
|
|
750
|
+
* - `filename.ext`
|
|
751
|
+
*
|
|
752
|
+
* 1. Resolve non-suffixed asset first.
|
|
753
|
+
* 2. If file is not exist, resolve suffixed path.
|
|
754
|
+
*/
|
|
755
|
+
if (scale === 1) try {
|
|
756
|
+
fs.statSync(assetPath);
|
|
757
|
+
return assetPath;
|
|
758
|
+
} catch {}
|
|
759
|
+
for (const suffixedPath of suffixedPaths) try {
|
|
760
|
+
fs.statSync(suffixedPath);
|
|
761
|
+
return suffixedPath;
|
|
762
|
+
} catch {}
|
|
763
|
+
throw new Error(`cannot resolve asset path for ${assetPath}`);
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* @see https://github.com/facebook/react-native/blob/0.83-stable/packages/community-cli-plugin/src/commands/bundle/assetPathUtils.js
|
|
767
|
+
*/
|
|
768
|
+
async function copyAssetsToDestination(options) {
|
|
769
|
+
const { assets, platform, assetsDir, preferNativePlatform } = options;
|
|
770
|
+
const context = {
|
|
771
|
+
platform,
|
|
772
|
+
preferNativePlatform
|
|
773
|
+
};
|
|
774
|
+
const mkdirWithAssertPath = (targetPath) => {
|
|
775
|
+
const dirname = path.dirname(targetPath);
|
|
776
|
+
fs.mkdirSync(dirname, { recursive: true });
|
|
777
|
+
};
|
|
778
|
+
return Promise.all(assets.map((asset) => {
|
|
779
|
+
return Promise.all(asset.scales.map(async (scale) => {
|
|
780
|
+
if (platform !== "android") {
|
|
781
|
+
const from$1 = resolveAssetPath(asset.id, context, scale);
|
|
782
|
+
const to$1 = path.join(assetsDir, getIosAssetDestinationPath(asset, scale));
|
|
783
|
+
mkdirWithAssertPath(to$1);
|
|
784
|
+
return fs.copyFileSync(from$1, to$1);
|
|
785
|
+
}
|
|
786
|
+
const from = resolveAssetPath(asset.id, context, scale);
|
|
787
|
+
const to = path.join(assetsDir, getAndroidAssetDestinationPath(asset, scale));
|
|
788
|
+
mkdirWithAssertPath(to);
|
|
789
|
+
fs.copyFileSync(from, to);
|
|
790
|
+
})).then(() => void 0);
|
|
791
|
+
})).then(() => void 0);
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* @see https://github.com/facebook/react-native/blob/0.83-stable/packages/community-cli-plugin/src/commands/bundle/getAssetDestPathIOS.js
|
|
795
|
+
*/
|
|
796
|
+
function getIosAssetDestinationPath(asset, scale) {
|
|
797
|
+
const suffix = scale === 1 ? "" : `@${scale}x`;
|
|
798
|
+
const fileName = `${asset.name + suffix}.${asset.type}`;
|
|
799
|
+
const devServerBasePath = asset.httpServerLocation.at(0) === "/" ? asset.httpServerLocation.slice(1) : asset.httpServerLocation;
|
|
800
|
+
return path.join(devServerBasePath.replace(/\.\.\//g, "_"), fileName);
|
|
801
|
+
}
|
|
802
|
+
function getAndroidAssetDestinationPath(asset, scale) {
|
|
803
|
+
const assetQualifierSuffix = ANDROID_ASSET_QUALIFIER[scale];
|
|
804
|
+
const assetName = `${asset.httpServerLocation.at(0) === "/" ? asset.httpServerLocation.slice(1) : asset.httpServerLocation}/${asset.name}`.toLowerCase().replace(/\//g, "_").replace(/(?:[^a-z0-9_])/g, "").replace(/^assets_/, "");
|
|
805
|
+
if (!assetQualifierSuffix) throw new Error(`invalid asset qualifier: ${asset.id}`);
|
|
806
|
+
return path.join(isDrawable(asset.type) ? `drawable-${assetQualifierSuffix}` : "raw", `${assetName}.${asset.type}`);
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* @see https://developer.android.com/guide/topics/resources/drawable-resource
|
|
810
|
+
*/
|
|
811
|
+
function isDrawable(type) {
|
|
812
|
+
return [
|
|
813
|
+
"gif",
|
|
814
|
+
"heic",
|
|
815
|
+
"heif",
|
|
816
|
+
"jpeg",
|
|
817
|
+
"jpg",
|
|
818
|
+
"ktx",
|
|
819
|
+
"png",
|
|
820
|
+
"webp",
|
|
821
|
+
"xml"
|
|
822
|
+
].includes(type);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
//#endregion
|
|
826
|
+
//#region src/core/plugins/utils/index.ts
|
|
827
|
+
const CACHE_HIT = Symbol("CACHE_HIT");
|
|
828
|
+
/**
|
|
829
|
+
* @internal
|
|
830
|
+
*/
|
|
831
|
+
function withPersistCache(plugins, options) {
|
|
832
|
+
const { enabled, sourceExtensions, context } = options;
|
|
833
|
+
if (!enabled) return plugins;
|
|
834
|
+
const includePattern = /* @__PURE__ */ new RegExp(`\\.(?:${sourceExtensions.join("|")})$`);
|
|
835
|
+
const excludePattern = /@oxc-project\+runtime/;
|
|
836
|
+
let cacheHits = 0;
|
|
837
|
+
const startMarker = {
|
|
838
|
+
name: "rollipop:persist-cache-start",
|
|
839
|
+
buildStart() {
|
|
840
|
+
cacheHits = 0;
|
|
841
|
+
},
|
|
842
|
+
buildEnd() {
|
|
843
|
+
this.debug(`Cache hits: ${cacheHits}`);
|
|
844
|
+
},
|
|
845
|
+
load: {
|
|
846
|
+
order: "pre",
|
|
847
|
+
filter: { id: {
|
|
848
|
+
include: includePattern,
|
|
849
|
+
exclude: excludePattern
|
|
850
|
+
} },
|
|
851
|
+
handler(id) {
|
|
852
|
+
const key = getCacheKey(id, context.id);
|
|
853
|
+
const cache = context.cache.get(key);
|
|
854
|
+
if (cache != null) {
|
|
855
|
+
cacheHits++;
|
|
856
|
+
return {
|
|
857
|
+
code: cache,
|
|
858
|
+
moduleType: "tsx",
|
|
859
|
+
meta: { [CACHE_HIT]: true }
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
const endMarker = {
|
|
866
|
+
name: "rollipop:persist-cache-end",
|
|
867
|
+
transform: {
|
|
868
|
+
order: "post",
|
|
869
|
+
filter: { id: {
|
|
870
|
+
include: includePattern,
|
|
871
|
+
exclude: excludePattern
|
|
872
|
+
} },
|
|
873
|
+
handler(code, id) {
|
|
874
|
+
const moduleInfo = this.getModuleInfo(id);
|
|
875
|
+
if (!(moduleInfo && isCacheHit(moduleInfo.meta))) {
|
|
876
|
+
const key = getCacheKey(id, context.id);
|
|
877
|
+
context.cache.set(key, code);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
return [
|
|
883
|
+
startMarker,
|
|
884
|
+
...plugins,
|
|
885
|
+
endMarker
|
|
886
|
+
];
|
|
887
|
+
}
|
|
888
|
+
function isCacheHit(meta) {
|
|
889
|
+
return CACHE_HIT in meta;
|
|
890
|
+
}
|
|
891
|
+
function getCacheKey(id, buildHash) {
|
|
892
|
+
const { mtimeMs } = fs.statSync(id);
|
|
893
|
+
return xxhash(`${id}${buildHash}${mtimeMs}`);
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Enhance a plugin to cache the result. (transform hook only)
|
|
897
|
+
*/
|
|
898
|
+
function cacheable(plugin) {
|
|
899
|
+
const originalTransform = plugin.transform;
|
|
900
|
+
if (typeof originalTransform === "function") plugin.transform = function(code, id, meta) {
|
|
901
|
+
const moduleInfo = this.getModuleInfo(id);
|
|
902
|
+
if (moduleInfo && isCacheHit(moduleInfo.meta)) return;
|
|
903
|
+
return originalTransform.call(this, code, id, meta);
|
|
904
|
+
};
|
|
905
|
+
if (typeof originalTransform === "object") plugin.transform = {
|
|
906
|
+
...originalTransform,
|
|
907
|
+
handler(code, id, meta) {
|
|
908
|
+
const moduleInfo = this.getModuleInfo(id);
|
|
909
|
+
if (moduleInfo && isCacheHit(moduleInfo.meta)) return;
|
|
910
|
+
return originalTransform.handler.call(this, code, id, meta);
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
return plugin;
|
|
914
|
+
}
|
|
915
|
+
const PluginUtils = Object.freeze({ cacheable });
|
|
916
|
+
|
|
917
|
+
//#endregion
|
|
918
|
+
//#region src/core/plugins/react-native-plugin.ts
|
|
919
|
+
var TransformFlags = /* @__PURE__ */ function(TransformFlags$1) {
|
|
920
|
+
TransformFlags$1[TransformFlags$1["SKIP_FLOW"] = 1] = "SKIP_FLOW";
|
|
921
|
+
TransformFlags$1[TransformFlags$1["NONE"] = 0] = "NONE";
|
|
922
|
+
return TransformFlags$1;
|
|
923
|
+
}(TransformFlags || {});
|
|
924
|
+
function reactNativePlugin(config, options) {
|
|
925
|
+
const { mode, flowFilter, codegenFilter, assetsDir, assetExtensions, assetRegistryPath } = options;
|
|
926
|
+
const assetExtensionRegex = /* @__PURE__ */ new RegExp(`\\.(?:${assetExtensions.join("|")})$`);
|
|
927
|
+
const codegenPlugin = {
|
|
928
|
+
name: "rollipop:react-native-codegen",
|
|
929
|
+
transform: {
|
|
930
|
+
order: "pre",
|
|
931
|
+
filter: codegenFilter,
|
|
932
|
+
handler(code, id) {
|
|
933
|
+
this.debug(`Transforming codegen native component ${id}`);
|
|
934
|
+
const result = babel.transformSync(code, {
|
|
935
|
+
filename: path$1.basename(id),
|
|
936
|
+
babelrc: false,
|
|
937
|
+
configFile: false,
|
|
938
|
+
sourceMaps: true,
|
|
939
|
+
parserOpts: { flow: "all" },
|
|
940
|
+
plugins: [
|
|
941
|
+
__require.resolve("babel-plugin-syntax-hermes-parser"),
|
|
942
|
+
__require.resolve("@babel/plugin-transform-flow-strip-types"),
|
|
943
|
+
[__require.resolve("@babel/plugin-syntax-typescript"), {
|
|
944
|
+
isTSX: id.endsWith("x"),
|
|
945
|
+
dts: false
|
|
946
|
+
}],
|
|
947
|
+
__require.resolve("@react-native/babel-plugin-codegen")
|
|
948
|
+
]
|
|
949
|
+
});
|
|
950
|
+
if (result?.code == null) throw new Error(`Failed to transform codegen native component: ${id}`);
|
|
951
|
+
return {
|
|
952
|
+
code: result.code,
|
|
953
|
+
map: result.map,
|
|
954
|
+
meta: setFlag(this, id, TransformFlags.SKIP_FLOW)
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
const stripFlowSyntaxPlugin = {
|
|
960
|
+
name: "rollipop:react-native-strip-flow-syntax",
|
|
961
|
+
transform: {
|
|
962
|
+
order: "pre",
|
|
963
|
+
filter: flowFilter,
|
|
964
|
+
handler(code, id) {
|
|
965
|
+
if (getFlags(this, id) & TransformFlags.SKIP_FLOW) return;
|
|
966
|
+
const result = stripFlowSyntax(code, id);
|
|
967
|
+
return {
|
|
968
|
+
code: result.code,
|
|
969
|
+
map: result.map,
|
|
970
|
+
moduleType: "tsx"
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
const blockScopingPlugin = {
|
|
976
|
+
name: "rollipop:react-native-block-scoping",
|
|
977
|
+
transform: {
|
|
978
|
+
order: "post",
|
|
979
|
+
handler(code, id) {
|
|
980
|
+
const result = blockScoping(code, id, options.dev);
|
|
981
|
+
return {
|
|
982
|
+
code: result.code,
|
|
983
|
+
map: result.map
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
const assets = [];
|
|
989
|
+
const assetPlugin = {
|
|
990
|
+
name: "rollipop:react-native-asset",
|
|
991
|
+
load: {
|
|
992
|
+
filter: { id: assetExtensionRegex },
|
|
993
|
+
async handler(id) {
|
|
994
|
+
this.debug(`Asset ${id} found`);
|
|
995
|
+
const assetData = await resolveScaledAssets({
|
|
996
|
+
projectRoot: config.root,
|
|
997
|
+
assetPath: id,
|
|
998
|
+
platform: options.platform,
|
|
999
|
+
preferNativePlatform: config.resolver.preferNativePlatform
|
|
1000
|
+
});
|
|
1001
|
+
assets.push(assetData);
|
|
1002
|
+
return { code: `
|
|
1003
|
+
module.exports = require('${assetRegistryPath}').registerAsset(${JSON.stringify(assetData)});
|
|
1004
|
+
` };
|
|
1005
|
+
}
|
|
1006
|
+
},
|
|
1007
|
+
buildStart() {
|
|
1008
|
+
assets.length = 0;
|
|
1009
|
+
},
|
|
1010
|
+
async buildEnd(error) {
|
|
1011
|
+
if (error || mode === "serve") return;
|
|
1012
|
+
if (assetsDir != null) {
|
|
1013
|
+
this.debug(`Copying assets to ${assetsDir}`);
|
|
1014
|
+
await copyAssetsToDestination({
|
|
1015
|
+
assets,
|
|
1016
|
+
assetsDir,
|
|
1017
|
+
platform: options.platform,
|
|
1018
|
+
preferNativePlatform: config.resolver.preferNativePlatform
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
const hmrClientImplement = fs.readFileSync(__require.resolve("@rollipop/core/hmr-client"), "utf-8");
|
|
1024
|
+
const hmrClientPath = __require.resolve(process.env.ROLLIPOP_HMR_CLIENT_PATH ?? DEFAULT_HMR_CLIENT_PATH, { paths: [config.root] });
|
|
1025
|
+
const replaceHMRClientPlugin = {
|
|
1026
|
+
name: "rollipop:react-native-replace-hmr-client",
|
|
1027
|
+
resolveId: {
|
|
1028
|
+
filter: { id: /\/HMRClient\.js$/ },
|
|
1029
|
+
async handler(id, importer) {
|
|
1030
|
+
const resolvedId = await this.resolve(id, importer, { skipSelf: true });
|
|
1031
|
+
if (resolvedId?.id === hmrClientPath) await this.load({ id: resolvedId.id });
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
load: {
|
|
1035
|
+
filter: { id: exactRegex(hmrClientPath) },
|
|
1036
|
+
handler(id) {
|
|
1037
|
+
this.debug(`Replacing HMR client: ${id}`);
|
|
1038
|
+
return hmrClientImplement;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
};
|
|
1042
|
+
const devServerPlugins = mode === "serve" ? [replaceHMRClientPlugin] : null;
|
|
1043
|
+
return [
|
|
1044
|
+
PluginUtils.cacheable(codegenPlugin),
|
|
1045
|
+
PluginUtils.cacheable(stripFlowSyntaxPlugin),
|
|
1046
|
+
PluginUtils.cacheable(blockScopingPlugin),
|
|
1047
|
+
assetPlugin,
|
|
1048
|
+
...devServerPlugins ?? []
|
|
1049
|
+
];
|
|
1050
|
+
}
|
|
1051
|
+
function setFlag(context, id, flag) {
|
|
1052
|
+
const moduleInfo = context.getModuleInfo(id);
|
|
1053
|
+
if (moduleInfo && hasFlags(moduleInfo.meta)) {
|
|
1054
|
+
moduleInfo.meta.flags |= flag;
|
|
1055
|
+
return moduleInfo.meta;
|
|
1056
|
+
} else return { meta: flag };
|
|
1057
|
+
}
|
|
1058
|
+
function hasFlags(meta) {
|
|
1059
|
+
return "flags" in meta;
|
|
1060
|
+
}
|
|
1061
|
+
function getFlags(context, id) {
|
|
1062
|
+
const moduleInfo = context.getModuleInfo(id);
|
|
1063
|
+
if (moduleInfo && hasFlags(moduleInfo.meta)) return moduleInfo.meta.flags;
|
|
1064
|
+
return TransformFlags.NONE;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
//#endregion
|
|
1068
|
+
//#region src/core/plugins/react-refresh-plugin.ts
|
|
1069
|
+
const DEFAULT_INCLUDE_REGEX = /\.[tj]sx?(?:$|\?)/;
|
|
1070
|
+
const DEFAULT_EXCLUDE_REGEX = /\/node_modules\//;
|
|
1071
|
+
const HAS_REFRESH_REGEX = /\$RefreshReg\$\(/;
|
|
1072
|
+
const ONLY_REACT_COMPONENT_REGEX = /extends\s+(?:React\.)?(?:Pure)?Component/;
|
|
1073
|
+
function reactRefreshPlugin(options) {
|
|
1074
|
+
const { include = DEFAULT_INCLUDE_REGEX, exclude = DEFAULT_EXCLUDE_REGEX } = options ?? {};
|
|
1075
|
+
return {
|
|
1076
|
+
name: "rollipop:react-refresh",
|
|
1077
|
+
transform: {
|
|
1078
|
+
filter: { id: {
|
|
1079
|
+
include,
|
|
1080
|
+
exclude
|
|
1081
|
+
} },
|
|
1082
|
+
handler(code, id, meta) {
|
|
1083
|
+
const { magicString } = meta;
|
|
1084
|
+
invariant(magicString != null, "magicString is not available");
|
|
1085
|
+
const { code: transformedCode } = transformSync(id, code, {
|
|
1086
|
+
sourcemap: true,
|
|
1087
|
+
jsx: {
|
|
1088
|
+
runtime: "automatic",
|
|
1089
|
+
development: true,
|
|
1090
|
+
refresh: {
|
|
1091
|
+
refreshReg: `${GLOBAL_IDENTIFIER}.$RefreshReg$`,
|
|
1092
|
+
refreshSig: `${GLOBAL_IDENTIFIER}.$RefreshSig$`
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
magicString.overwrite(0, magicString.length(), transformedCode);
|
|
1097
|
+
applyRefreshWrapper(magicString, {
|
|
1098
|
+
id,
|
|
1099
|
+
hasRefresh: HAS_REFRESH_REGEX.test(transformedCode),
|
|
1100
|
+
onlyReactComponent: ONLY_REACT_COMPONENT_REGEX.test(transformedCode)
|
|
1101
|
+
});
|
|
1102
|
+
return { code: magicString };
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
function applyRefreshWrapper(s, options) {
|
|
1108
|
+
const { id, hasRefresh, onlyReactComponent } = options;
|
|
1109
|
+
if (!(hasRefresh || onlyReactComponent)) return;
|
|
1110
|
+
if (hasRefresh) s.prepend(`
|
|
1111
|
+
var __prev$RefreshReg$ = global.$RefreshReg$;
|
|
1112
|
+
var __prev$RefreshSig$ = global.$RefreshSig$;
|
|
1113
|
+
global.$RefreshReg$ = function(type, id) { return __ReactRefresh.register(type, ${JSON.stringify(id)} + ' ' + id) }
|
|
1114
|
+
global.$RefreshSig$ = function() { return __ReactRefresh.createSignatureFunctionForTransform(); }
|
|
1115
|
+
`);
|
|
1116
|
+
s.append(`
|
|
1117
|
+
if (import.meta.hot) {
|
|
1118
|
+
if (import.meta.hot.refresh == null) throw new Error('react-refresh runtime is not initialized');
|
|
1119
|
+
import.meta.hot.accept((nextExports) => {
|
|
1120
|
+
if (!nextExports) return;
|
|
1121
|
+
if (import.meta.hot.refreshUtils.isReactRefreshBoundary(nextExports)) {
|
|
1122
|
+
import.meta.hot.refresh.performReactRefresh();
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
1125
|
+
}`);
|
|
1126
|
+
if (hasRefresh) s.append(`
|
|
1127
|
+
global.$RefreshReg$ = __prev$RefreshReg$;
|
|
1128
|
+
global.$RefreshSig$ = __prev$RefreshSig$;
|
|
1129
|
+
`);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
//#endregion
|
|
1133
|
+
//#region src/core/plugins/shim.ts
|
|
1134
|
+
function shim() {
|
|
1135
|
+
return { name: `rollipop:shim-${shim.index++}` };
|
|
1136
|
+
}
|
|
1137
|
+
shim.index = 0;
|
|
1138
|
+
|
|
1139
|
+
//#endregion
|
|
1140
|
+
//#region src/core/plugins/prelude-plugin.ts
|
|
1141
|
+
const IS_ENTRY = Symbol("IS_ENTRY");
|
|
1142
|
+
function preludePlugin(options) {
|
|
1143
|
+
if (options.modulePaths.length === 0) return shim();
|
|
1144
|
+
const preludeImportStatements = options.modulePaths.map((modulePath) => `import '${modulePath}';`).join("\n");
|
|
1145
|
+
let processed = false;
|
|
1146
|
+
return {
|
|
1147
|
+
name: "rollipop:prelude",
|
|
1148
|
+
buildStart() {
|
|
1149
|
+
processed = false;
|
|
1150
|
+
},
|
|
1151
|
+
resolveId: { handler: (source, _importer, extraOptions) => {
|
|
1152
|
+
if (extraOptions.isEntry) return {
|
|
1153
|
+
id: source,
|
|
1154
|
+
meta: { [IS_ENTRY]: true }
|
|
1155
|
+
};
|
|
1156
|
+
} },
|
|
1157
|
+
load: { handler(id) {
|
|
1158
|
+
if (processed) return;
|
|
1159
|
+
const moduleInfo = this.getModuleInfo(id);
|
|
1160
|
+
if (moduleInfo && isEntry(moduleInfo.meta)) {
|
|
1161
|
+
this.debug(`Prelude plugin found entry ${id}`);
|
|
1162
|
+
const modifiedSource = [preludeImportStatements, fs.readFileSync(id, "utf-8")].join("\n");
|
|
1163
|
+
processed = true;
|
|
1164
|
+
return modifiedSource;
|
|
1165
|
+
}
|
|
1166
|
+
} }
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
function isEntry(meta) {
|
|
1170
|
+
return IS_ENTRY in meta;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
//#endregion
|
|
1174
|
+
//#region src/core/plugins/status-plugin.ts
|
|
1175
|
+
function statusPlugin(options) {
|
|
1176
|
+
let totalModules = options?.initialTotalModules ?? 0;
|
|
1177
|
+
let startedAt = 0;
|
|
1178
|
+
let transformedModules = 0;
|
|
1179
|
+
let unknownTotalModules = totalModules === 0;
|
|
1180
|
+
return {
|
|
1181
|
+
name: "rollipop:status",
|
|
1182
|
+
buildStart() {
|
|
1183
|
+
startedAt = performance.now();
|
|
1184
|
+
transformedModules = 0;
|
|
1185
|
+
options?.onStart?.();
|
|
1186
|
+
},
|
|
1187
|
+
buildEnd(error) {
|
|
1188
|
+
totalModules = transformedModules;
|
|
1189
|
+
unknownTotalModules = false;
|
|
1190
|
+
options?.onEnd?.({
|
|
1191
|
+
error,
|
|
1192
|
+
totalModules,
|
|
1193
|
+
duration: performance.now() - startedAt
|
|
1194
|
+
});
|
|
1195
|
+
},
|
|
1196
|
+
resolveId: {
|
|
1197
|
+
order: "post",
|
|
1198
|
+
handler(id) {
|
|
1199
|
+
options?.onResolve?.(id);
|
|
1200
|
+
}
|
|
1201
|
+
},
|
|
1202
|
+
transform: {
|
|
1203
|
+
order: "post",
|
|
1204
|
+
handler(_code, id) {
|
|
1205
|
+
++transformedModules;
|
|
1206
|
+
if (!unknownTotalModules && totalModules < transformedModules) totalModules = transformedModules;
|
|
1207
|
+
options?.onTransform?.({
|
|
1208
|
+
id,
|
|
1209
|
+
totalModules: unknownTotalModules ? void 0 : totalModules,
|
|
1210
|
+
transformedModules
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
},
|
|
1214
|
+
watchChange(id) {
|
|
1215
|
+
options?.onWatchChange?.(id);
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
//#endregion
|
|
1221
|
+
//#region src/core/plugins/json-plugin.ts
|
|
1222
|
+
function jsonPlugin() {
|
|
1223
|
+
return {
|
|
1224
|
+
name: "rollipop:json",
|
|
1225
|
+
load: {
|
|
1226
|
+
filter: { id: /\.json$/ },
|
|
1227
|
+
handler(id) {
|
|
1228
|
+
const rawJson = fs.readFileSync(id, "utf-8");
|
|
1229
|
+
return {
|
|
1230
|
+
code: jsonToEsm(JSON.parse(rawJson)),
|
|
1231
|
+
moduleType: "js"
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
function jsonToEsm(data) {
|
|
1238
|
+
const declarations = [];
|
|
1239
|
+
const exports = [];
|
|
1240
|
+
const exportDefaultMappings = [];
|
|
1241
|
+
Object.entries(data).forEach(([key, value], index) => {
|
|
1242
|
+
const identifier = `_${index}`;
|
|
1243
|
+
declarations.push(`const ${identifier} = ${JSON.stringify(value)};`);
|
|
1244
|
+
exports.push(`export { ${identifier} as "${key}" };`);
|
|
1245
|
+
exportDefaultMappings.push(`"${key}":${identifier}`);
|
|
1246
|
+
});
|
|
1247
|
+
return [
|
|
1248
|
+
...declarations,
|
|
1249
|
+
...exports,
|
|
1250
|
+
`export default {${exportDefaultMappings.join(",")}};`
|
|
1251
|
+
].join("\n");
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
//#endregion
|
|
1255
|
+
//#region src/core/plugins/svg-plugin.ts
|
|
1256
|
+
function svgPlugin(options) {
|
|
1257
|
+
if (!options.enabled) return shim();
|
|
1258
|
+
return {
|
|
1259
|
+
name: "rollipop:svg",
|
|
1260
|
+
load: {
|
|
1261
|
+
filter: { id: /\.svg$/ },
|
|
1262
|
+
async handler(id) {
|
|
1263
|
+
return {
|
|
1264
|
+
code: await transform(fs.readFileSync(id, "utf-8"), {
|
|
1265
|
+
template: defaultTemplate,
|
|
1266
|
+
plugins: [__require.resolve("@svgr/plugin-jsx")],
|
|
1267
|
+
native: true
|
|
1268
|
+
}, { filePath: id }),
|
|
1269
|
+
moduleType: "jsx"
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
1275
|
+
const SVG_COMPONENT_NAME = "SvgLogo";
|
|
1276
|
+
const defaultTemplate = (variables, { tpl }) => {
|
|
1277
|
+
return tpl`${variables.imports};
|
|
1278
|
+
|
|
1279
|
+
${variables.interfaces};
|
|
1280
|
+
|
|
1281
|
+
const ${SVG_COMPONENT_NAME} = (${variables.props}) => (
|
|
1282
|
+
${variables.jsx}
|
|
1283
|
+
);
|
|
1284
|
+
|
|
1285
|
+
export default ${SVG_COMPONENT_NAME};`;
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
//#endregion
|
|
1289
|
+
//#region src/core/plugins/index.ts
|
|
1290
|
+
var plugins_exports = /* @__PURE__ */ __export({
|
|
1291
|
+
applyRefreshWrapper: () => applyRefreshWrapper,
|
|
1292
|
+
json: () => jsonPlugin,
|
|
1293
|
+
prelude: () => preludePlugin,
|
|
1294
|
+
reactNative: () => reactNativePlugin,
|
|
1295
|
+
reactRefresh: () => reactRefreshPlugin,
|
|
1296
|
+
status: () => statusPlugin,
|
|
1297
|
+
svg: () => svgPlugin
|
|
1298
|
+
});
|
|
1299
|
+
|
|
1300
|
+
//#endregion
|
|
1301
|
+
//#region src/core/rolldown.ts
|
|
1302
|
+
const rolldownLogger = new Logger("rolldown");
|
|
1303
|
+
function resolveBuildOptions(buildOptions) {
|
|
1304
|
+
return merge({
|
|
1305
|
+
dev: true,
|
|
1306
|
+
cache: true,
|
|
1307
|
+
minify: false
|
|
1308
|
+
}, buildOptions);
|
|
1309
|
+
}
|
|
1310
|
+
resolveRolldownOptions.cache = /* @__PURE__ */ new Map();
|
|
1311
|
+
async function resolveRolldownOptions(context, config, buildOptions) {
|
|
1312
|
+
const cachedOptions = resolveRolldownOptions.cache.get(context.id);
|
|
1313
|
+
if (cachedOptions != null) return cachedOptions;
|
|
1314
|
+
const { platform, dev: dev$1, cache, minify } = resolveBuildOptions(buildOptions);
|
|
1315
|
+
const { sourceExtensions, assetExtensions, preferNativePlatform, ...rolldownResolve } = config.resolver;
|
|
1316
|
+
const { prelude: preludePaths, polyfills } = config.serializer;
|
|
1317
|
+
const { flow, ...rolldownTransform } = config.transformer;
|
|
1318
|
+
const { codegen, assetRegistryPath } = config.reactNative;
|
|
1319
|
+
const resolvedSourceExtensions = config.transformer.svg ? [...sourceExtensions, "svg"] : sourceExtensions;
|
|
1320
|
+
const resolvedAssetExtensions = config.transformer.svg ? assetExtensions.filter((extension) => extension !== "svg") : assetExtensions;
|
|
1321
|
+
const mergedResolveOptions = merge({ extensions: getResolveExtensions({
|
|
1322
|
+
sourceExtensions: resolvedSourceExtensions,
|
|
1323
|
+
assetExtensions: resolvedAssetExtensions,
|
|
1324
|
+
platform,
|
|
1325
|
+
preferNativePlatform
|
|
1326
|
+
}) }, rolldownResolve);
|
|
1327
|
+
const mergedTransformOptions = merge({
|
|
1328
|
+
target: "es2015",
|
|
1329
|
+
jsx: {
|
|
1330
|
+
runtime: "automatic",
|
|
1331
|
+
development: dev$1
|
|
1332
|
+
},
|
|
1333
|
+
define: {
|
|
1334
|
+
__DEV__: asLiteral(dev$1),
|
|
1335
|
+
"process.env.NODE_ENV": asLiteral(nodeEnvironment(dev$1)),
|
|
1336
|
+
global: asIdentifier(GLOBAL_IDENTIFIER)
|
|
1337
|
+
},
|
|
1338
|
+
typescript: { removeClassFieldsWithoutInitializer: true },
|
|
1339
|
+
assumptions: { setPublicClassFields: true },
|
|
1340
|
+
helpers: { mode: "Runtime" }
|
|
1341
|
+
}, rolldownTransform);
|
|
1342
|
+
const devServerPlugins = context.mode === "serve" ? [reactRefreshPlugin()] : [];
|
|
1343
|
+
const statusPreset = config.terminal.status === "progress" ? statusPresets.progressBar(config.reporter, context, `[${platform}, ${buildOptions.dev ? "dev" : "prod"}]`) : statusPresets.compat(config.reporter);
|
|
1344
|
+
const finalOptions = await applyDangerouslyOverrideOptionsFinalizer(config, {
|
|
1345
|
+
cwd: config.root,
|
|
1346
|
+
input: config.entry,
|
|
1347
|
+
platform: "neutral",
|
|
1348
|
+
treeshake: true,
|
|
1349
|
+
resolve: mergedResolveOptions,
|
|
1350
|
+
transform: mergedTransformOptions,
|
|
1351
|
+
plugins: withPersistCache([
|
|
1352
|
+
preludePlugin({ modulePaths: preludePaths }),
|
|
1353
|
+
reactNativePlugin(config, {
|
|
1354
|
+
dev: dev$1,
|
|
1355
|
+
platform,
|
|
1356
|
+
mode: context.mode,
|
|
1357
|
+
codegenFilter: codegen.filter,
|
|
1358
|
+
flowFilter: flow.filter,
|
|
1359
|
+
assetsDir: buildOptions.assetsDir,
|
|
1360
|
+
assetExtensions: resolvedAssetExtensions,
|
|
1361
|
+
assetRegistryPath
|
|
1362
|
+
}),
|
|
1363
|
+
svgPlugin({ enabled: config.transformer.svg }),
|
|
1364
|
+
jsonPlugin(),
|
|
1365
|
+
statusPlugin(statusPreset),
|
|
1366
|
+
...devServerPlugins ?? [],
|
|
1367
|
+
...config.plugins ?? []
|
|
1368
|
+
], {
|
|
1369
|
+
enabled: cache,
|
|
1370
|
+
context,
|
|
1371
|
+
sourceExtensions
|
|
1372
|
+
}),
|
|
1373
|
+
checks: {
|
|
1374
|
+
eval: false,
|
|
1375
|
+
pluginTimings: false
|
|
1376
|
+
},
|
|
1377
|
+
logLevel: isDebugEnabled() ? "debug" : "info",
|
|
1378
|
+
onLog(level, log) {
|
|
1379
|
+
const { message, code } = log;
|
|
1380
|
+
const logArgs = [code, message].filter(isNotNil);
|
|
1381
|
+
switch (level) {
|
|
1382
|
+
case "debug":
|
|
1383
|
+
rolldownLogger.debug(...logArgs);
|
|
1384
|
+
break;
|
|
1385
|
+
case "info":
|
|
1386
|
+
rolldownLogger.info(...logArgs);
|
|
1387
|
+
break;
|
|
1388
|
+
case "warn":
|
|
1389
|
+
rolldownLogger.warn(...logArgs);
|
|
1390
|
+
break;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
}, {
|
|
1394
|
+
postBanner: [...getGlobalVariables(dev$1, context.mode)].join("\n"),
|
|
1395
|
+
intro: [...loadPolyfills(polyfills)].join("\n"),
|
|
1396
|
+
file: buildOptions.outfile,
|
|
1397
|
+
minify,
|
|
1398
|
+
format: "iife",
|
|
1399
|
+
keepNames: true,
|
|
1400
|
+
sourcemap: true
|
|
1401
|
+
});
|
|
1402
|
+
resolveRolldownOptions.cache.set(context.id, finalOptions);
|
|
1403
|
+
return finalOptions;
|
|
1404
|
+
}
|
|
1405
|
+
function getResolveExtensions({ platform, sourceExtensions, assetExtensions, preferNativePlatform }) {
|
|
1406
|
+
const supportedExtensions = [...sourceExtensions, ...assetExtensions];
|
|
1407
|
+
return [...[platform, preferNativePlatform ? "native" : null].filter(isNotNil).map((platform$1) => {
|
|
1408
|
+
return supportedExtensions.map((extension) => `.${platform$1}.${extension}`);
|
|
1409
|
+
}), ...supportedExtensions.map((extension) => `.${extension}`)].flat();
|
|
1410
|
+
}
|
|
1411
|
+
function loadPolyfills(polyfills) {
|
|
1412
|
+
return polyfills.map((polyfill) => {
|
|
1413
|
+
if (typeof polyfill === "string") return fs.readFileSync(polyfill, "utf-8");
|
|
1414
|
+
const path$2 = "path" in polyfill ? polyfill.path : void 0;
|
|
1415
|
+
const content = "code" in polyfill ? polyfill.code : fs.readFileSync(polyfill.path, "utf-8");
|
|
1416
|
+
return polyfill.type === "iife" ? iife(content, path$2) : content;
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1419
|
+
async function applyDangerouslyOverrideOptionsFinalizer(config, inputOptions, outputOptions) {
|
|
1420
|
+
if (typeof config.dangerously_overrideRolldownOptions === "function") return await config.dangerously_overrideRolldownOptions({
|
|
1421
|
+
input: inputOptions,
|
|
1422
|
+
output: outputOptions
|
|
1423
|
+
});
|
|
1424
|
+
return {
|
|
1425
|
+
input: merge(inputOptions, config.dangerously_overrideRolldownOptions?.input ?? {}),
|
|
1426
|
+
output: merge(outputOptions, config.dangerously_overrideRolldownOptions?.output ?? {})
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
function getOverrideOptionsForDevServer() {
|
|
1430
|
+
return {
|
|
1431
|
+
input: {
|
|
1432
|
+
transform: { jsx: { development: true } },
|
|
1433
|
+
experimental: {
|
|
1434
|
+
devMode: { implement: fs.readFileSync(__require.resolve("@rollipop/core/hmr-runtime"), "utf-8") },
|
|
1435
|
+
incrementalBuild: true,
|
|
1436
|
+
strictExecutionOrder: true,
|
|
1437
|
+
nativeMagicString: true
|
|
1438
|
+
},
|
|
1439
|
+
treeshake: false
|
|
1440
|
+
},
|
|
1441
|
+
output: { sourcemap: true }
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
//#endregion
|
|
1446
|
+
//#region src/core/bundler.ts
|
|
1447
|
+
var Bundler = class Bundler {
|
|
1448
|
+
static async devEngine(config, buildOptions, devEngineOptions) {
|
|
1449
|
+
const mode = "serve";
|
|
1450
|
+
const resolvedBuildOptions = {
|
|
1451
|
+
...buildOptions,
|
|
1452
|
+
dev: true
|
|
1453
|
+
};
|
|
1454
|
+
const { input = {}, output = {} } = await resolveRolldownOptions({
|
|
1455
|
+
...Bundler.createContext(mode, config, resolvedBuildOptions),
|
|
1456
|
+
mode
|
|
1457
|
+
}, config, resolvedBuildOptions);
|
|
1458
|
+
const devServerOptions = getOverrideOptionsForDevServer();
|
|
1459
|
+
return await dev(merge(input, devServerOptions.input), merge(output, devServerOptions.output), devEngineOptions);
|
|
1460
|
+
}
|
|
1461
|
+
static createId(config, buildOptions) {
|
|
1462
|
+
return createId(config, buildOptions);
|
|
1463
|
+
}
|
|
1464
|
+
static createContext(mode, config, buildOptions) {
|
|
1465
|
+
const id = `${mode}:${Bundler.createId(config, buildOptions)}`;
|
|
1466
|
+
return {
|
|
1467
|
+
id,
|
|
1468
|
+
cache: new FileSystemCache(path.join(getCachePath(config.root), id)),
|
|
1469
|
+
storage: FileStorage.getInstance(config.root)
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
constructor(config) {
|
|
1473
|
+
this.config = config;
|
|
1474
|
+
Logo.printOnce();
|
|
1475
|
+
}
|
|
1476
|
+
async build(buildOptions) {
|
|
1477
|
+
const mode = "build";
|
|
1478
|
+
const contextBase = Bundler.createContext(mode, this.config, buildOptions);
|
|
1479
|
+
const { config } = this;
|
|
1480
|
+
const { input, output } = await resolveRolldownOptions({
|
|
1481
|
+
...contextBase,
|
|
1482
|
+
mode
|
|
1483
|
+
}, config, buildOptions);
|
|
1484
|
+
const rolldownBuildOptions = {
|
|
1485
|
+
...input,
|
|
1486
|
+
output,
|
|
1487
|
+
write: true
|
|
1488
|
+
};
|
|
1489
|
+
const chunk = (await rolldown.build(rolldownBuildOptions)).output[0];
|
|
1490
|
+
invariant(chunk, "Bundled chunk is not found");
|
|
1491
|
+
return chunk;
|
|
1492
|
+
}
|
|
1493
|
+
};
|
|
1494
|
+
|
|
1495
|
+
//#endregion
|
|
1496
|
+
//#region src/reporter.ts
|
|
1497
|
+
var TerminalReporter = class {
|
|
1498
|
+
logger = new Logger("app");
|
|
1499
|
+
update(event) {
|
|
1500
|
+
if (event.type === "client_log") {
|
|
1501
|
+
if (event.level === "group" || event.level === "groupCollapsed") {
|
|
1502
|
+
this.logger.info(...event.data);
|
|
1503
|
+
return;
|
|
1504
|
+
} else if (event.level === "groupEnd") return;
|
|
1505
|
+
this.logger[event.level](...event.data);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
|
|
1510
|
+
//#endregion
|
|
1511
|
+
//#region src/utils/node-resolve.ts
|
|
1512
|
+
function resolvePackagePath(basePath, packageName) {
|
|
1513
|
+
let packagePath = null;
|
|
1514
|
+
try {
|
|
1515
|
+
packagePath = resolvePackagePathWithNodeRequire(basePath, packageName);
|
|
1516
|
+
if (packagePath) return packagePath;
|
|
1517
|
+
} catch {}
|
|
1518
|
+
try {
|
|
1519
|
+
packagePath = resolvePackagePathWithNodeRequire(basePath, packageName, "");
|
|
1520
|
+
if (packagePath) return packagePath;
|
|
1521
|
+
} catch {}
|
|
1522
|
+
throw new Error(`Failed to resolve package path for '${packageName}'`);
|
|
1523
|
+
}
|
|
1524
|
+
function resolvePackagePathWithNodeRequire(basePath, packageName, lookupSubpath = "package.json") {
|
|
1525
|
+
const lookupPath = lookupSubpath ? `/${lookupSubpath}` : "";
|
|
1526
|
+
const resolvedPath = __require.resolve(`${packageName}${lookupPath}`, { paths: [basePath] });
|
|
1527
|
+
const root = path.parse(resolvedPath).root;
|
|
1528
|
+
let currentPath = path.dirname(resolvedPath);
|
|
1529
|
+
while (currentPath !== root) {
|
|
1530
|
+
if (fs.existsSync(path.join(currentPath, "package.json"))) return currentPath;
|
|
1531
|
+
currentPath = path.dirname(currentPath);
|
|
1532
|
+
}
|
|
1533
|
+
return null;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
//#endregion
|
|
1537
|
+
//#region src/config/defaults.ts
|
|
1538
|
+
function getDefaultConfig(basePath, context) {
|
|
1539
|
+
const reactNativePath = resolvePackagePath(basePath, "react-native");
|
|
1540
|
+
const isDevServer = context.command === "start";
|
|
1541
|
+
return {
|
|
1542
|
+
root: basePath,
|
|
1543
|
+
entry: "index.js",
|
|
1544
|
+
resolver: {
|
|
1545
|
+
sourceExtensions: DEFAULT_SOURCE_EXTENSIONS,
|
|
1546
|
+
assetExtensions: DEFAULT_ASSET_EXTENSIONS,
|
|
1547
|
+
mainFields: DEFAULT_RESOLVER_MAIN_FIELDS,
|
|
1548
|
+
conditionNames: DEFAULT_RESOLVER_CONDITION_NAMES,
|
|
1549
|
+
preferNativePlatform: true
|
|
1550
|
+
},
|
|
1551
|
+
transformer: {
|
|
1552
|
+
svg: true,
|
|
1553
|
+
flow: { filter: {
|
|
1554
|
+
id: /\.jsx?$/,
|
|
1555
|
+
code: /@flow/
|
|
1556
|
+
} }
|
|
1557
|
+
},
|
|
1558
|
+
serializer: {
|
|
1559
|
+
prelude: [getInitializeCorePath(basePath)],
|
|
1560
|
+
polyfills: [...getPolyfillScriptPaths(reactNativePath).map((path$2) => ({
|
|
1561
|
+
type: "iife",
|
|
1562
|
+
code: stripFlowSyntax(fs.readFileSync(path$2, "utf-8"), path$2).code
|
|
1563
|
+
})), isDevServer ? __require.resolve("@rollipop/core/hmr-shims") : void 0].filter(isNotNil)
|
|
1564
|
+
},
|
|
1565
|
+
watcher: {
|
|
1566
|
+
skipWrite: true,
|
|
1567
|
+
useDebounce: true,
|
|
1568
|
+
debounceDuration: 50
|
|
1569
|
+
},
|
|
1570
|
+
reactNative: {
|
|
1571
|
+
codegen: { filter: { code: /codegenNativeComponent/ } },
|
|
1572
|
+
assetRegistryPath: DEFAULT_ASSET_REGISTRY_PATH
|
|
1573
|
+
},
|
|
1574
|
+
terminal: { status: process.stderr.isTTY ? "progress" : "compat" },
|
|
1575
|
+
reporter: new TerminalReporter()
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
//#endregion
|
|
1580
|
+
//#region src/config/define-config.ts
|
|
1581
|
+
function defineConfig(userConfig) {
|
|
1582
|
+
return userConfig;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
//#endregion
|
|
1586
|
+
//#region src/config/merge-config.ts
|
|
1587
|
+
function mergeConfig(baseConfig, overrideConfig) {
|
|
1588
|
+
return mergeWith(baseConfig, overrideConfig, (target, source, key) => {
|
|
1589
|
+
if (key === "reporter") return source.reporter ?? target.reporter;
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
//#endregion
|
|
1594
|
+
//#region src/config/load-config.ts
|
|
1595
|
+
const CONFIG_FILE_NAME = "rollipop";
|
|
1596
|
+
async function loadConfig(options = {}) {
|
|
1597
|
+
const { cwd = process.cwd(), configFile, context = {} } = options;
|
|
1598
|
+
const defaultConfig = getDefaultConfig(cwd, context);
|
|
1599
|
+
const commonOptions = {
|
|
1600
|
+
context: {
|
|
1601
|
+
...context,
|
|
1602
|
+
defaultConfig
|
|
1603
|
+
},
|
|
1604
|
+
rcFile: false
|
|
1605
|
+
};
|
|
1606
|
+
const { config: userConfig } = await c12.loadConfig(configFile ? {
|
|
1607
|
+
configFile: path.resolve(cwd, configFile),
|
|
1608
|
+
configFileRequired: true
|
|
1609
|
+
} : {
|
|
1610
|
+
cwd,
|
|
1611
|
+
defaultConfig,
|
|
1612
|
+
name: CONFIG_FILE_NAME,
|
|
1613
|
+
...commonOptions
|
|
1614
|
+
});
|
|
1615
|
+
return mergeConfig(defaultConfig, userConfig);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
//#endregion
|
|
1619
|
+
export { assets_exports as AssetUtils, Bundler, TerminalReporter as DefaultReporter, PluginUtils, defineConfig, getDefaultConfig, loadConfig, mergeConfig, plugins_exports as plugins, rolldown, rolldownExperimental };
|