@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 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 import_node_fs3 = require("fs");
28
- var import_node_path5 = __toESM(require("path"));
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/web.ts
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 JS_RE = /\.(m|c)?(t|j)sx?$/;
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: JS_RE }, async (args) => {
341
- if (!shouldTransform(args.path)) {
342
- return null;
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 code = await fs2.readFile(args.path, "utf8");
345
- const result = await babel.transformAsync(code, {
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
- // Needed by Reanimated on web per docs
353
- "@babel/plugin-transform-export-namespace-from",
354
- // Handle Flow types present in some RN libs
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
- // Let Babel parse TS/JSX/Flow; keep it broad
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
- // biome-ignore lint/suspicious/noExplicitAny: <explanation>
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 fs3 = __toESM(require("fs/promises"));
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
- if (!args.path.includes(`${path5.sep}react-native${path5.sep}`)) {
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 fs3.readFile(args.path, "utf8");
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 = import_node_path4.default.join(root, "package.json");
458
- const packageJson = JSON.parse(await (0, import_promises3.readFile)(packageJsonPath, "utf-8"));
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 = import_node_path4.default.join(root, "build", "entrypoints", "web", "index.tsx");
492
- await (0, import_promises3.mkdir)(import_node_path4.default.dirname(entryFile), { recursive: true });
493
- await (0, import_promises3.writeFile)(entryFile, entry, "utf-8");
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 = import_node_path4.default.join(root, "build", "output", "web");
499
- await (0, import_promises3.mkdir)(outDir, { recursive: true });
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: import_node_path4.default.join(outDir, "main.js"),
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: import_node_path4.default.join(args.resolveDir, "native-whop-core"),
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, import_promises3.writeFile)(import_node_path4.default.join(outDir, "index.html"), html, "utf-8");
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 = import_node_path4.default.join(root, "build", "output", "web");
605
- const mainJsFile = import_node_path4.default.join(fullDirectory, "main.js");
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, import_promises3.readFile)(mainJsFile);
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, import_promises3.readFile)(mainJsFile);
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] # uploads the existing build directory to whop.
700
- whop-react-native clean # cleans the build directory.`
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
- promises.push(buildAndPublish(root, "ios", opts));
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
- promises.push(buildAndPublish(root, "android", opts));
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 = import_node_path5.default.join(root, "build");
728
- if ((0, import_node_fs3.existsSync)(buildDirectory)) {
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 = import_node_path5.default.join(root, "build", "entrypoints");
735
- if ((0, import_node_fs3.existsSync)(buildDirectory)) {
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 = import_node_path5.default.dirname(file);
1197
+ const root = import_node_path6.default.dirname(file);
748
1198
  return root;
749
1199
  }
750
1200
  async function handleInstall() {