@whop/react-native 0.1.15 → 0.2.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/dist/cli/index.js +523 -73
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +512 -62
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/rspack-flow-loader.js +30 -0
- package/dist/cli/rspack-reanimated-loader.js +55 -0
- package/package.json +4 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "../chunk-KIXXMEFR.mjs";
|
|
5
5
|
|
|
6
6
|
// src/cli/index.ts
|
|
7
|
-
import { existsSync as
|
|
7
|
+
import { existsSync as existsSync3 } from "fs";
|
|
8
8
|
import path7 from "path";
|
|
9
9
|
import { parseArgs } from "util";
|
|
10
10
|
import { findUp as findUp2 } from "find-up";
|
|
@@ -298,79 +298,498 @@ var CustomReporter = class {
|
|
|
298
298
|
}
|
|
299
299
|
};
|
|
300
300
|
|
|
301
|
+
// src/cli/mobile-rspack.ts
|
|
302
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2 } from "fs";
|
|
303
|
+
import { mkdir as mkdir2, rename as rename2, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
304
|
+
import path4 from "path";
|
|
305
|
+
var flowLoaderPath = path4.join(__dirname, "rspack-flow-loader.js");
|
|
306
|
+
var reanimatedLoaderPath = path4.join(__dirname, "rspack-reanimated-loader.js");
|
|
307
|
+
async function loadRspack(root) {
|
|
308
|
+
try {
|
|
309
|
+
const rspackPath = __require.resolve("@rspack/core", { paths: [root] });
|
|
310
|
+
return __require(rspackPath);
|
|
311
|
+
} catch {
|
|
312
|
+
return __require("@rspack/core");
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async function loadRepack(root) {
|
|
316
|
+
try {
|
|
317
|
+
const repackPath = __require.resolve("@callstack/repack", { paths: [root] });
|
|
318
|
+
return __require(repackPath);
|
|
319
|
+
} catch {
|
|
320
|
+
return __require("@callstack/repack");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function isFlowPreTransformed(root) {
|
|
324
|
+
const markerFile = path4.join(root, "node_modules/.flow-pretransformed");
|
|
325
|
+
return existsSync2(markerFile);
|
|
326
|
+
}
|
|
327
|
+
function getReactNativeInitModules(root) {
|
|
328
|
+
const reactNativePath = path4.dirname(
|
|
329
|
+
__require.resolve("react-native/package.json", { paths: [root] })
|
|
330
|
+
);
|
|
331
|
+
const getPolyfills = __require(path4.join(reactNativePath, "rn-get-polyfills.js"));
|
|
332
|
+
const polyfills = getPolyfills();
|
|
333
|
+
const initializeCore = path4.join(reactNativePath, "Libraries/Core/InitializeCore.js");
|
|
334
|
+
return [...polyfills, initializeCore];
|
|
335
|
+
}
|
|
336
|
+
async function createHermesPolyfills(root, platform) {
|
|
337
|
+
const polyfillDir = path4.join(root, "build", "entrypoints", platform);
|
|
338
|
+
await mkdir2(polyfillDir, { recursive: true });
|
|
339
|
+
const polyfillPath = path4.join(polyfillDir, "hermes-polyfills.js");
|
|
340
|
+
const polyfillCode = `
|
|
341
|
+
// Polyfills for Web APIs not available in Hermes
|
|
342
|
+
// This runs after React Native's InitializeCore, so 'global' is available
|
|
343
|
+
|
|
344
|
+
(function(g) {
|
|
345
|
+
// TextDecoder/TextEncoder (needed by jose/JWT libraries)
|
|
346
|
+
if (typeof g.TextDecoder === 'undefined') {
|
|
347
|
+
g.TextDecoder = function TextDecoder(encoding) {
|
|
348
|
+
if (encoding && encoding !== 'utf-8' && encoding !== 'utf8') {
|
|
349
|
+
throw new Error('TextDecoder polyfill only supports UTF-8');
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
g.TextDecoder.prototype.decode = function(input) {
|
|
353
|
+
if (!input) return '';
|
|
354
|
+
var bytes = new Uint8Array(input.buffer || input);
|
|
355
|
+
var result = '';
|
|
356
|
+
for (var i = 0; i < bytes.length; i++) {
|
|
357
|
+
result += String.fromCharCode(bytes[i]);
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
return decodeURIComponent(escape(result));
|
|
361
|
+
} catch (e) {
|
|
362
|
+
return result;
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (typeof g.TextEncoder === 'undefined') {
|
|
368
|
+
g.TextEncoder = function TextEncoder() {};
|
|
369
|
+
g.TextEncoder.prototype.encode = function(str) {
|
|
370
|
+
var utf8 = unescape(encodeURIComponent(str || ''));
|
|
371
|
+
var result = new Uint8Array(utf8.length);
|
|
372
|
+
for (var i = 0; i < utf8.length; i++) {
|
|
373
|
+
result[i] = utf8.charCodeAt(i);
|
|
374
|
+
}
|
|
375
|
+
return result;
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// URL polyfill (minimal, for libraries that need it before RN's kicks in)
|
|
380
|
+
if (typeof g.URL === 'undefined') {
|
|
381
|
+
g.URL = function URL(url, base) {
|
|
382
|
+
if (base && typeof base === 'string' && !/^[a-z][a-z0-9+.-]*:/i.test(url)) {
|
|
383
|
+
url = base.replace(/\\/[^\\/]*$/, '/') + url;
|
|
384
|
+
}
|
|
385
|
+
this.href = url || '';
|
|
386
|
+
var match = this.href.match(/^([a-z][a-z0-9+.-]*:)?\\/\\/([^\\/:]*)(:[0-9]+)?(\\/[^?#]*)?(\\?[^#]*)?(#.*)?$/i);
|
|
387
|
+
if (match) {
|
|
388
|
+
this.protocol = match[1] || '';
|
|
389
|
+
this.hostname = match[2] || '';
|
|
390
|
+
this.port = (match[3] || '').slice(1);
|
|
391
|
+
this.pathname = match[4] || '/';
|
|
392
|
+
this.search = match[5] || '';
|
|
393
|
+
this.hash = match[6] || '';
|
|
394
|
+
this.host = this.hostname + (this.port ? ':' + this.port : '');
|
|
395
|
+
this.origin = this.protocol + '//' + this.host;
|
|
396
|
+
} else {
|
|
397
|
+
this.protocol = ''; this.hostname = ''; this.port = '';
|
|
398
|
+
this.pathname = this.href; this.search = ''; this.hash = '';
|
|
399
|
+
this.host = ''; this.origin = '';
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
g.URL.prototype.toString = function() { return this.href; };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// URLSearchParams polyfill (minimal)
|
|
406
|
+
if (typeof g.URLSearchParams === 'undefined') {
|
|
407
|
+
g.URLSearchParams = function URLSearchParams(init) {
|
|
408
|
+
this._params = [];
|
|
409
|
+
if (typeof init === 'string') {
|
|
410
|
+
init = init.replace(/^\\?/, '');
|
|
411
|
+
var pairs = init.split('&');
|
|
412
|
+
for (var i = 0; i < pairs.length; i++) {
|
|
413
|
+
var pair = pairs[i].split('=');
|
|
414
|
+
if (pair[0]) {
|
|
415
|
+
this._params.push([decodeURIComponent(pair[0]), decodeURIComponent(pair[1] || '')]);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
g.URLSearchParams.prototype.get = function(name) {
|
|
421
|
+
for (var i = 0; i < this._params.length; i++) {
|
|
422
|
+
if (this._params[i][0] === name) return this._params[i][1];
|
|
423
|
+
}
|
|
424
|
+
return null;
|
|
425
|
+
};
|
|
426
|
+
g.URLSearchParams.prototype.toString = function() {
|
|
427
|
+
return this._params.map(function(p) {
|
|
428
|
+
return encodeURIComponent(p[0]) + '=' + encodeURIComponent(p[1]);
|
|
429
|
+
}).join('&');
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
})(typeof globalThis !== 'undefined' ? globalThis : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this);
|
|
433
|
+
`;
|
|
434
|
+
await writeFile2(polyfillPath, polyfillCode);
|
|
435
|
+
return polyfillPath;
|
|
436
|
+
}
|
|
437
|
+
async function bundleWithRspack(root, platform) {
|
|
438
|
+
const rspackModule = await loadRspack(root);
|
|
439
|
+
const { rspack } = rspackModule;
|
|
440
|
+
const Repack = await loadRepack(root);
|
|
441
|
+
const entryFile = await makeEntrypoint2(root, platform);
|
|
442
|
+
const rnInitModules = getReactNativeInitModules(root);
|
|
443
|
+
const hermesPolyfills = await createHermesPolyfills(root, platform);
|
|
444
|
+
const outputDir = path4.join(root, "build", "output", platform);
|
|
445
|
+
await mkdir2(outputDir, { recursive: true });
|
|
446
|
+
const outputFile = path4.join(outputDir, "main_js_bundle.js");
|
|
447
|
+
const flowPreTransformed = isFlowPreTransformed(root);
|
|
448
|
+
if (flowPreTransformed) {
|
|
449
|
+
console.log(` \u2139\uFE0E [${platform}] using pre-transformed Flow files (fast path)`);
|
|
450
|
+
}
|
|
451
|
+
const moduleRules = [
|
|
452
|
+
// Allow extensionless imports in ESM modules (e.g., @rn-primitives)
|
|
453
|
+
{
|
|
454
|
+
test: /\.m?js$/,
|
|
455
|
+
resolve: {
|
|
456
|
+
fullySpecified: false
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
];
|
|
460
|
+
if (flowPreTransformed) {
|
|
461
|
+
moduleRules.push({
|
|
462
|
+
test: /\.[cm]?[jt]sx?$/,
|
|
463
|
+
use: {
|
|
464
|
+
loader: "builtin:swc-loader",
|
|
465
|
+
options: {
|
|
466
|
+
jsc: {
|
|
467
|
+
parser: {
|
|
468
|
+
syntax: "typescript",
|
|
469
|
+
tsx: true
|
|
470
|
+
},
|
|
471
|
+
transform: {
|
|
472
|
+
react: {
|
|
473
|
+
runtime: "automatic"
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
type: "javascript/auto"
|
|
480
|
+
});
|
|
481
|
+
} else {
|
|
482
|
+
moduleRules.push(
|
|
483
|
+
// Strip Flow types from react-native core files FIRST
|
|
484
|
+
{
|
|
485
|
+
test: /\.jsx?$/,
|
|
486
|
+
include: [
|
|
487
|
+
/node_modules[/\\]react-native[/\\]/,
|
|
488
|
+
/node_modules[/\\]@react-native[/\\]/
|
|
489
|
+
],
|
|
490
|
+
use: [
|
|
491
|
+
// Then process with SWC
|
|
492
|
+
{
|
|
493
|
+
loader: "builtin:swc-loader",
|
|
494
|
+
options: {
|
|
495
|
+
jsc: {
|
|
496
|
+
parser: {
|
|
497
|
+
syntax: "ecmascript",
|
|
498
|
+
jsx: true
|
|
499
|
+
},
|
|
500
|
+
transform: {
|
|
501
|
+
react: {
|
|
502
|
+
runtime: "automatic"
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
// First strip Flow types with Babel
|
|
509
|
+
{
|
|
510
|
+
loader: flowLoaderPath
|
|
511
|
+
}
|
|
512
|
+
],
|
|
513
|
+
type: "javascript/auto"
|
|
514
|
+
},
|
|
515
|
+
// App code and other node_modules: JS/TS/JSX/TSX via SWC
|
|
516
|
+
{
|
|
517
|
+
test: /\.[cm]?[jt]sx?$/,
|
|
518
|
+
exclude: [
|
|
519
|
+
/node_modules[/\\]react-native[/\\]/,
|
|
520
|
+
/node_modules[/\\]@react-native[/\\]/
|
|
521
|
+
],
|
|
522
|
+
use: {
|
|
523
|
+
loader: "builtin:swc-loader",
|
|
524
|
+
options: {
|
|
525
|
+
jsc: {
|
|
526
|
+
parser: {
|
|
527
|
+
syntax: "typescript",
|
|
528
|
+
tsx: true
|
|
529
|
+
},
|
|
530
|
+
transform: {
|
|
531
|
+
react: {
|
|
532
|
+
runtime: "automatic"
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
type: "javascript/auto"
|
|
539
|
+
}
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
moduleRules.unshift({
|
|
543
|
+
test: /\.[jt]sx?$/,
|
|
544
|
+
include: [
|
|
545
|
+
/node_modules[/\\]react-native-reanimated[/\\]/,
|
|
546
|
+
// Also include app code that might have worklets
|
|
547
|
+
path4.join(root, "src")
|
|
548
|
+
],
|
|
549
|
+
use: {
|
|
550
|
+
loader: reanimatedLoaderPath
|
|
551
|
+
},
|
|
552
|
+
enforce: "pre"
|
|
553
|
+
// Run before other loaders
|
|
554
|
+
});
|
|
555
|
+
moduleRules.push({
|
|
556
|
+
test: /\.(png|jpg|jpeg|gif|webp|svg)$/,
|
|
557
|
+
type: "asset/inline"
|
|
558
|
+
});
|
|
559
|
+
const cacheDir = path4.join(root, "node_modules/.cache/rspack");
|
|
560
|
+
const hasCacheDir = existsSync2(cacheDir);
|
|
561
|
+
if (hasCacheDir) {
|
|
562
|
+
console.log(` \u2139\uFE0E [${platform}] using pre-warmed Rspack cache`);
|
|
563
|
+
}
|
|
564
|
+
const config2 = {
|
|
565
|
+
mode: "production",
|
|
566
|
+
context: root,
|
|
567
|
+
// Disable default target - we're targeting React Native's Hermes runtime
|
|
568
|
+
target: false,
|
|
569
|
+
// Entry order: RN init -> Hermes polyfills -> app code
|
|
570
|
+
entry: [...rnInitModules, hermesPolyfills, entryFile],
|
|
571
|
+
output: {
|
|
572
|
+
path: outputDir,
|
|
573
|
+
filename: "main_js_bundle.js",
|
|
574
|
+
publicPath: "/",
|
|
575
|
+
clean: false,
|
|
576
|
+
// Use 'self' as global object (initialized by our banner)
|
|
577
|
+
globalObject: "self",
|
|
578
|
+
// Disable async chunk loading - RN needs single bundle
|
|
579
|
+
chunkLoading: false,
|
|
580
|
+
chunkFormat: false
|
|
581
|
+
},
|
|
582
|
+
// Enable caching (required for experiments.cache)
|
|
583
|
+
cache: true,
|
|
584
|
+
// Persistent filesystem cache (Rspack 1.3+ format)
|
|
585
|
+
experiments: {
|
|
586
|
+
cache: {
|
|
587
|
+
type: "persistent",
|
|
588
|
+
version: "rspack-rn-v2",
|
|
589
|
+
storage: {
|
|
590
|
+
type: "filesystem",
|
|
591
|
+
directory: cacheDir
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
resolve: {
|
|
596
|
+
// Get Re.Pack's resolve options for React Native
|
|
597
|
+
...Repack.getResolveOptions({ platform }),
|
|
598
|
+
// Ensure we can resolve from the app's node_modules
|
|
599
|
+
modules: [
|
|
600
|
+
path4.join(root, "node_modules"),
|
|
601
|
+
"node_modules"
|
|
602
|
+
],
|
|
603
|
+
// Use react-native field first in package.json
|
|
604
|
+
mainFields: ["react-native", "browser", "module", "main"],
|
|
605
|
+
// Re-enable exports field resolution (Re.Pack disables it)
|
|
606
|
+
conditionNames: ["react-native", "import", "require", "default"],
|
|
607
|
+
exportsFields: ["exports"],
|
|
608
|
+
// Platform-specific extensions - order matters! Most specific first
|
|
609
|
+
// Re.Pack's extensions have literal [platform] which doesn't work
|
|
610
|
+
extensions: [
|
|
611
|
+
`.${platform}.ts`,
|
|
612
|
+
`.${platform}.tsx`,
|
|
613
|
+
`.${platform}.js`,
|
|
614
|
+
`.${platform}.jsx`,
|
|
615
|
+
".native.ts",
|
|
616
|
+
".native.tsx",
|
|
617
|
+
".native.js",
|
|
618
|
+
".native.jsx",
|
|
619
|
+
".ts",
|
|
620
|
+
".tsx",
|
|
621
|
+
".js",
|
|
622
|
+
".jsx",
|
|
623
|
+
".json"
|
|
624
|
+
],
|
|
625
|
+
// Alias react-native-reanimated to lib/module to avoid TypeScript source
|
|
626
|
+
alias: {
|
|
627
|
+
...Repack.getResolveOptions({ platform }).alias,
|
|
628
|
+
"react-native-reanimated": path4.join(root, "node_modules/react-native-reanimated/lib/module")
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
resolveLoader: {
|
|
632
|
+
// Allow resolving loaders from the CLI package's node_modules
|
|
633
|
+
modules: [
|
|
634
|
+
path4.join(__dirname, ".."),
|
|
635
|
+
// dist/ folder
|
|
636
|
+
path4.dirname(__dirname),
|
|
637
|
+
// package root
|
|
638
|
+
"node_modules"
|
|
639
|
+
]
|
|
640
|
+
},
|
|
641
|
+
module: {
|
|
642
|
+
rules: moduleRules
|
|
643
|
+
},
|
|
644
|
+
plugins: [
|
|
645
|
+
// NOTE: We intentionally do NOT use RepackTargetPlugin because it sets
|
|
646
|
+
// chunkLoading: 'jsonp' which creates separate chunk files that can't be
|
|
647
|
+
// loaded in React Native. Instead, we manually set up what we need.
|
|
648
|
+
// Initialize 'self' global (normally done by RepackTargetPlugin)
|
|
649
|
+
new rspack.BannerPlugin({
|
|
650
|
+
raw: true,
|
|
651
|
+
entryOnly: true,
|
|
652
|
+
banner: `var self = self || this || new Function("return this")() || {};`
|
|
653
|
+
}),
|
|
654
|
+
// Define globals for React Native
|
|
655
|
+
new rspack.DefinePlugin({
|
|
656
|
+
__DEV__: JSON.stringify(false),
|
|
657
|
+
"process.env.NODE_ENV": JSON.stringify("production")
|
|
658
|
+
})
|
|
659
|
+
],
|
|
660
|
+
optimization: {
|
|
661
|
+
minimize: false,
|
|
662
|
+
// Keep readable for debugging
|
|
663
|
+
// Disable code splitting - React Native needs a single bundle
|
|
664
|
+
splitChunks: false,
|
|
665
|
+
runtimeChunk: false
|
|
666
|
+
},
|
|
667
|
+
devtool: false,
|
|
668
|
+
stats: "errors-warnings"
|
|
669
|
+
};
|
|
670
|
+
await new Promise((resolve, reject) => {
|
|
671
|
+
rspack(config2, (err, stats) => {
|
|
672
|
+
if (err) {
|
|
673
|
+
reject(err);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (stats?.hasErrors()) {
|
|
677
|
+
const info = stats.toJson();
|
|
678
|
+
console.error(info.errors);
|
|
679
|
+
reject(new Error("Rspack build failed"));
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
resolve();
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
const finalFile = path4.join(outputDir, "main_js_bundle.hbc");
|
|
686
|
+
if (!existsSync2(outputFile)) {
|
|
687
|
+
throw new Error(
|
|
688
|
+
`[${platform}] Rspack completed but bundle file was not created at ${outputFile}. Check the build output for warnings or errors.`
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
await rename2(outputFile, finalFile);
|
|
692
|
+
const outputFiles = readdirSync2(outputDir);
|
|
693
|
+
console.log(` \u2139\uFE0E [${platform}] files created:`, outputFiles.join(", "));
|
|
694
|
+
for (const file of outputFiles) {
|
|
695
|
+
if (file !== "main_js_bundle.hbc" && file !== "assets") {
|
|
696
|
+
await unlink(path4.join(outputDir, file));
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
console.log(` \u2714\uFE0E [${platform}] bundle created (rspack)`);
|
|
700
|
+
}
|
|
701
|
+
async function makeEntrypoint2(root, platform) {
|
|
702
|
+
const entrypoint = path4.join(
|
|
703
|
+
root,
|
|
704
|
+
"build",
|
|
705
|
+
"entrypoints",
|
|
706
|
+
platform,
|
|
707
|
+
"index.js"
|
|
708
|
+
);
|
|
709
|
+
const files = await getSupportedAppViewTypes(root);
|
|
710
|
+
const pascalCase = (str) => str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
711
|
+
const imports = files.map(
|
|
712
|
+
(file) => `import { ${pascalCase(file)} } from "../../../src/views/${file}";`
|
|
713
|
+
);
|
|
714
|
+
const registry = files.map(
|
|
715
|
+
(file) => `AppRegistry.registerComponent("${pascalCase(file)}", () => ${pascalCase(
|
|
716
|
+
file
|
|
717
|
+
)});`
|
|
718
|
+
);
|
|
719
|
+
const entrypointContent = `import { AppRegistry } from "react-native";
|
|
720
|
+
${imports.join("\n")}
|
|
721
|
+
|
|
722
|
+
${registry.join("\n")}
|
|
723
|
+
`;
|
|
724
|
+
const entrypointDir = path4.dirname(entrypoint);
|
|
725
|
+
await mkdir2(entrypointDir, { recursive: true });
|
|
726
|
+
await writeFile2(entrypoint, entrypointContent, "utf-8");
|
|
727
|
+
console.log(` \u2714\uFE0E [${platform}] entrypoint created`);
|
|
728
|
+
return entrypoint;
|
|
729
|
+
}
|
|
730
|
+
|
|
301
731
|
// src/cli/web.ts
|
|
302
|
-
import { mkdir as
|
|
732
|
+
import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
303
733
|
import path6 from "path";
|
|
304
734
|
import { build } from "esbuild";
|
|
305
735
|
|
|
306
736
|
// src/cli/reanimated-bable.ts
|
|
307
|
-
import * as fs2 from "fs/promises";
|
|
308
|
-
import * as path4 from "path";
|
|
309
737
|
import * as babel from "@babel/core";
|
|
310
|
-
var
|
|
738
|
+
var WORKLET_DIRECTIVE_RX = /['"]worklet['"]/;
|
|
311
739
|
function reanimatedBabelPlugin() {
|
|
312
|
-
const shouldTransform = (p) => {
|
|
313
|
-
if (!JS_RE.test(p)) return false;
|
|
314
|
-
if (p.includes(`${path4.sep}react-native-reanimated${path4.sep}`))
|
|
315
|
-
return true;
|
|
316
|
-
if (p.includes(`${path4.sep}node_modules${path4.sep}`)) return false;
|
|
317
|
-
return p.includes(`${path4.sep}src${path4.sep}`);
|
|
318
|
-
};
|
|
319
740
|
return {
|
|
320
741
|
name: "reanimated-babel",
|
|
321
742
|
setup(b) {
|
|
322
|
-
b.onLoad({ filter:
|
|
323
|
-
|
|
324
|
-
|
|
743
|
+
b.onLoad({ filter: /\.[jt]sx?$/ }, async (args) => {
|
|
744
|
+
const fs3 = await import("fs/promises");
|
|
745
|
+
const source = await fs3.readFile(args.path, "utf8");
|
|
746
|
+
if (!WORKLET_DIRECTIVE_RX.test(source)) {
|
|
747
|
+
return void 0;
|
|
325
748
|
}
|
|
326
|
-
const
|
|
327
|
-
const
|
|
749
|
+
const isTS = args.path.endsWith(".ts") || args.path.endsWith(".tsx");
|
|
750
|
+
const hasJSX = args.path.endsWith("x") || args.path.endsWith(".js");
|
|
751
|
+
const syntaxPlugins = [];
|
|
752
|
+
if (isTS) {
|
|
753
|
+
syntaxPlugins.push(["@babel/plugin-syntax-typescript", { isTSX: args.path.endsWith("x") }]);
|
|
754
|
+
} else if (hasJSX) {
|
|
755
|
+
syntaxPlugins.push("@babel/plugin-syntax-jsx");
|
|
756
|
+
}
|
|
757
|
+
const result = await babel.transformAsync(source, {
|
|
328
758
|
filename: args.path,
|
|
329
|
-
sourceMaps: false,
|
|
330
759
|
babelrc: false,
|
|
331
760
|
configFile: false,
|
|
332
|
-
// ORDER MATTERS: Reanimated plugin MUST BE LAST
|
|
333
761
|
plugins: [
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
[
|
|
338
|
-
"@babel/plugin-transform-flow-strip-types",
|
|
339
|
-
{ allowDeclareFields: true }
|
|
340
|
-
],
|
|
341
|
-
// MUST be last
|
|
342
|
-
[
|
|
343
|
-
"react-native-reanimated/plugin",
|
|
344
|
-
{ relativeSourceLocation: true }
|
|
345
|
-
]
|
|
762
|
+
...syntaxPlugins,
|
|
763
|
+
// Transform worklets
|
|
764
|
+
"react-native-reanimated/plugin"
|
|
346
765
|
],
|
|
347
766
|
presets: [],
|
|
348
767
|
// esbuild handles TS/JSX syntax; no preset-env/preset-react
|
|
349
768
|
caller: { name: "esbuild" },
|
|
350
|
-
|
|
351
|
-
parserOpts: { plugins: ["jsx", "typescript"] },
|
|
352
|
-
generatorOpts: { decoratorsBeforeExport: true }
|
|
769
|
+
sourceMaps: "inline"
|
|
353
770
|
});
|
|
771
|
+
if (!result?.code) {
|
|
772
|
+
return void 0;
|
|
773
|
+
}
|
|
774
|
+
const ext = args.path.split(".").pop() ?? "ts";
|
|
775
|
+
const loaderMap = {
|
|
776
|
+
tsx: "tsx",
|
|
777
|
+
jsx: "jsx",
|
|
778
|
+
ts: "ts",
|
|
779
|
+
js: "jsx"
|
|
780
|
+
// .js files in RN often contain JSX
|
|
781
|
+
};
|
|
354
782
|
return {
|
|
355
|
-
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
|
356
783
|
contents: result.code,
|
|
357
|
-
|
|
358
|
-
loader: pickLoader(args.path)
|
|
784
|
+
loader: loaderMap[ext] ?? "ts"
|
|
359
785
|
};
|
|
360
786
|
});
|
|
361
787
|
}
|
|
362
788
|
};
|
|
363
789
|
}
|
|
364
|
-
function pickLoader(file) {
|
|
365
|
-
const ext = path4.extname(file).toLowerCase();
|
|
366
|
-
if (ext === ".tsx") return "tsx";
|
|
367
|
-
if (ext === ".ts") return "ts";
|
|
368
|
-
if (ext === ".jsx") return "jsx";
|
|
369
|
-
return "jsx";
|
|
370
|
-
}
|
|
371
790
|
|
|
372
791
|
// src/cli/strip-flow.ts
|
|
373
|
-
import * as
|
|
792
|
+
import * as fs2 from "fs/promises";
|
|
374
793
|
import * as path5 from "path";
|
|
375
794
|
import * as babel2 from "@babel/core";
|
|
376
795
|
function stripFlowWithBabel() {
|
|
@@ -380,10 +799,11 @@ function stripFlowWithBabel() {
|
|
|
380
799
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
381
800
|
setup(b) {
|
|
382
801
|
b.onLoad({ filter }, async (args) => {
|
|
383
|
-
|
|
802
|
+
const isReactNative = args.path.includes(`${path5.sep}react-native${path5.sep}`) || args.path.includes(`${path5.sep}@react-native${path5.sep}`);
|
|
803
|
+
if (!isReactNative) {
|
|
384
804
|
return null;
|
|
385
805
|
}
|
|
386
|
-
const code = await
|
|
806
|
+
const code = await fs2.readFile(args.path, "utf8");
|
|
387
807
|
const out = await babel2.transformAsync(code, {
|
|
388
808
|
filename: args.path,
|
|
389
809
|
babelrc: false,
|
|
@@ -437,7 +857,7 @@ function toPascalCase(str) {
|
|
|
437
857
|
async function makeWebEntrypoint(root) {
|
|
438
858
|
const files = await getSupportedAppViewTypes(root);
|
|
439
859
|
const packageJsonPath = path6.join(root, "package.json");
|
|
440
|
-
const packageJson = JSON.parse(await
|
|
860
|
+
const packageJson = JSON.parse(await readFile2(packageJsonPath, "utf-8"));
|
|
441
861
|
const hasReactNativeReanimated = packageJson.dependencies?.["react-native-reanimated"];
|
|
442
862
|
const imports = files.map(
|
|
443
863
|
(file) => `import { ${toPascalCase(file)} } from "../../../src/views/${file}";`
|
|
@@ -471,14 +891,14 @@ const root = document.getElementById("root") || (() => {
|
|
|
471
891
|
AppRegistry.runApplication(viewType, { rootTag: root });
|
|
472
892
|
`;
|
|
473
893
|
const entryFile = path6.join(root, "build", "entrypoints", "web", "index.tsx");
|
|
474
|
-
await
|
|
475
|
-
await
|
|
894
|
+
await mkdir3(path6.dirname(entryFile), { recursive: true });
|
|
895
|
+
await writeFile3(entryFile, entry, "utf-8");
|
|
476
896
|
return entryFile;
|
|
477
897
|
}
|
|
478
898
|
async function bundleWeb(root) {
|
|
479
899
|
const entry = await makeWebEntrypoint(root);
|
|
480
900
|
const outDir = path6.join(root, "build", "output", "web");
|
|
481
|
-
await
|
|
901
|
+
await mkdir3(outDir, { recursive: true });
|
|
482
902
|
await build({
|
|
483
903
|
entryPoints: [entry],
|
|
484
904
|
outfile: path6.join(outDir, "main.js"),
|
|
@@ -564,7 +984,7 @@ async function bundleWeb(root) {
|
|
|
564
984
|
<script type="module" src="./main.js"></script>
|
|
565
985
|
</body>
|
|
566
986
|
</html>`;
|
|
567
|
-
await
|
|
987
|
+
await writeFile3(path6.join(outDir, "index.html"), html, "utf-8");
|
|
568
988
|
console.log(" \u2714\uFE0E [web] bundle created at build/output/web/main.js");
|
|
569
989
|
}
|
|
570
990
|
async function buildAndPublish2(root, {
|
|
@@ -586,11 +1006,11 @@ async function createWebBuild(root) {
|
|
|
586
1006
|
const fullDirectory = path6.join(root, "build", "output", "web");
|
|
587
1007
|
const mainJsFile = path6.join(fullDirectory, "main.js");
|
|
588
1008
|
try {
|
|
589
|
-
await
|
|
1009
|
+
await readFile2(mainJsFile);
|
|
590
1010
|
} catch {
|
|
591
1011
|
throw new Error(`main.js not found in ${fullDirectory}`);
|
|
592
1012
|
}
|
|
593
|
-
const buf = await
|
|
1013
|
+
const buf = await readFile2(mainJsFile);
|
|
594
1014
|
const checksum = await getChecksum(buf);
|
|
595
1015
|
console.log(` \u2714\uFE0E [web] build checksummed: ${checksum}`);
|
|
596
1016
|
const fileName = `rnweb_${checksum}.js`;
|
|
@@ -648,6 +1068,9 @@ async function main() {
|
|
|
648
1068
|
},
|
|
649
1069
|
web: {
|
|
650
1070
|
type: "boolean"
|
|
1071
|
+
},
|
|
1072
|
+
metro: {
|
|
1073
|
+
type: "boolean"
|
|
651
1074
|
}
|
|
652
1075
|
},
|
|
653
1076
|
strict: true,
|
|
@@ -676,10 +1099,13 @@ async function main() {
|
|
|
676
1099
|
} else {
|
|
677
1100
|
console.error(
|
|
678
1101
|
`Usage:
|
|
679
|
-
whop-react-native ship [--ios] [--android] [--web] # runs build and then publishes it as a dev build to whop.
|
|
680
|
-
whop-react-native build [--ios] [--android] [--web] # builds your app into a distributable bundle in the build/ directory.
|
|
681
|
-
whop-react-native upload [--ios] [--android] [--web]
|
|
682
|
-
whop-react-native clean
|
|
1102
|
+
whop-react-native ship [--ios] [--android] [--web] [--metro] # runs build and then publishes it as a dev build to whop.
|
|
1103
|
+
whop-react-native build [--ios] [--android] [--web] [--metro] # builds your app into a distributable bundle in the build/ directory.
|
|
1104
|
+
whop-react-native upload [--ios] [--android] [--web] # uploads the existing build directory to whop.
|
|
1105
|
+
whop-react-native clean # cleans the build directory.
|
|
1106
|
+
|
|
1107
|
+
Options:
|
|
1108
|
+
--metro Use Metro instead of Rspack for mobile builds (slower, for compatibility)`
|
|
683
1109
|
);
|
|
684
1110
|
process.exit(1);
|
|
685
1111
|
}
|
|
@@ -692,29 +1118,53 @@ async function main() {
|
|
|
692
1118
|
}
|
|
693
1119
|
}
|
|
694
1120
|
const didProvidePlatform = args.values.ios || args.values.android || args.values.web;
|
|
1121
|
+
const useMetro = args.values.metro || envBool("WRN_USE_METRO");
|
|
695
1122
|
const opts = { shouldBuild, shouldUpload };
|
|
696
1123
|
const promises = [];
|
|
1124
|
+
const bundler = useMetro ? "metro" : "rspack";
|
|
1125
|
+
if (shouldBuild) {
|
|
1126
|
+
console.log(` \u2139\uFE0E using ${bundler} for mobile builds`);
|
|
1127
|
+
}
|
|
697
1128
|
if (args.values.ios || !didProvidePlatform) {
|
|
698
|
-
|
|
1129
|
+
if (useMetro) {
|
|
1130
|
+
promises.push(buildAndPublish(root, "ios", opts));
|
|
1131
|
+
} else {
|
|
1132
|
+
promises.push(buildAndPublishWithRspack(root, "ios", opts));
|
|
1133
|
+
}
|
|
699
1134
|
}
|
|
700
1135
|
if (args.values.android || !didProvidePlatform) {
|
|
701
|
-
|
|
1136
|
+
if (useMetro) {
|
|
1137
|
+
promises.push(buildAndPublish(root, "android", opts));
|
|
1138
|
+
} else {
|
|
1139
|
+
promises.push(buildAndPublishWithRspack(root, "android", opts));
|
|
1140
|
+
}
|
|
702
1141
|
}
|
|
703
1142
|
if (args.values.web || !didProvidePlatform) {
|
|
704
1143
|
promises.push(buildAndPublish2(root, opts));
|
|
705
1144
|
}
|
|
706
1145
|
await Promise.all(promises);
|
|
707
1146
|
}
|
|
1147
|
+
async function buildAndPublishWithRspack(root, platform, {
|
|
1148
|
+
shouldBuild = true,
|
|
1149
|
+
shouldUpload = true
|
|
1150
|
+
}) {
|
|
1151
|
+
if (shouldBuild) {
|
|
1152
|
+
await bundleWithRspack(root, platform);
|
|
1153
|
+
}
|
|
1154
|
+
if (shouldUpload) {
|
|
1155
|
+
await createMobileBuild(root, platform);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
708
1158
|
async function cleanBuildDirectory(root) {
|
|
709
1159
|
const buildDirectory = path7.join(root, "build");
|
|
710
|
-
if (
|
|
1160
|
+
if (existsSync3(buildDirectory)) {
|
|
711
1161
|
await rimraf(buildDirectory);
|
|
712
1162
|
}
|
|
713
1163
|
console.log(" \u2714\uFE0E cleaned build directory");
|
|
714
1164
|
}
|
|
715
1165
|
async function cleanEntrypointsDirectory(root) {
|
|
716
1166
|
const buildDirectory = path7.join(root, "build", "entrypoints");
|
|
717
|
-
if (
|
|
1167
|
+
if (existsSync3(buildDirectory)) {
|
|
718
1168
|
await rimraf(buildDirectory);
|
|
719
1169
|
}
|
|
720
1170
|
console.log(" \u2714\uFE0E cleaned build entrypoints directory");
|