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