@whop/react-native 0.0.10 → 0.0.12
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 +369 -40
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +366 -37
- package/dist/cli/index.mjs.map +1 -1
- package/dist/lib/index.d.mts +15 -1
- package/dist/lib/index.d.ts +15 -1
- package/dist/lib/index.js +305 -5
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +295 -5
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/web.mjs +214 -0
- package/dist/lib/web.mjs.map +1 -0
- package/package.json +64 -4
package/dist/cli/index.js
CHANGED
|
@@ -25,15 +25,16 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
|
|
26
26
|
// src/cli/index.ts
|
|
27
27
|
var import_node_fs2 = require("fs");
|
|
28
|
-
var
|
|
28
|
+
var import_node_path4 = __toESM(require("path"));
|
|
29
29
|
var import_node_util = require("util");
|
|
30
30
|
var import_find_up2 = require("find-up");
|
|
31
|
+
var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
|
|
31
32
|
var import_rimraf = require("rimraf");
|
|
32
33
|
|
|
33
34
|
// src/cli/mobile.ts
|
|
34
35
|
var import_node_fs = require("fs");
|
|
35
|
-
var
|
|
36
|
-
var
|
|
36
|
+
var import_promises2 = require("fs/promises");
|
|
37
|
+
var import_node_path2 = __toESM(require("path"));
|
|
37
38
|
var import_metro_config = require("@react-native/metro-config");
|
|
38
39
|
var import_find_up = require("find-up");
|
|
39
40
|
var import_jszip = __toESM(require("jszip"));
|
|
@@ -84,7 +85,25 @@ async function getChecksum(data) {
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
// src/cli/valid-view-type.ts
|
|
88
|
+
var import_promises = require("fs/promises");
|
|
89
|
+
var import_node_path = __toESM(require("path"));
|
|
87
90
|
var VALID_VIEW_TYPES = ["experience-view", "discover-view"];
|
|
91
|
+
async function getSupportedAppViewTypes(root) {
|
|
92
|
+
const views = await (0, import_promises.readdir)(import_node_path.default.join(root, "src", "views"), {
|
|
93
|
+
withFileTypes: true,
|
|
94
|
+
recursive: false
|
|
95
|
+
});
|
|
96
|
+
const files = views.filter((file) => file.isFile()).map((file) => file.name.split(".")[0]).filter((file) => !!file);
|
|
97
|
+
const validViews = files.filter(
|
|
98
|
+
(file) => VALID_VIEW_TYPES.includes(file)
|
|
99
|
+
);
|
|
100
|
+
if (validViews.length === 0) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`No valid views found, please create a view in the src/views folder and name it with a valid view type: ${VALID_VIEW_TYPES.join(", ")}`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return validViews;
|
|
106
|
+
}
|
|
88
107
|
|
|
89
108
|
// src/cli/mobile.ts
|
|
90
109
|
async function buildAndPublish(root, platform, {
|
|
@@ -103,14 +122,14 @@ async function buildAndPublish(root, platform, {
|
|
|
103
122
|
}
|
|
104
123
|
async function bundle(root, platform) {
|
|
105
124
|
await makeEntrypoint(root, platform);
|
|
106
|
-
const outputFile =
|
|
125
|
+
const outputFile = import_node_path2.default.join(
|
|
107
126
|
root,
|
|
108
127
|
"build",
|
|
109
128
|
"output",
|
|
110
129
|
platform,
|
|
111
130
|
"main_js_bundle"
|
|
112
131
|
);
|
|
113
|
-
await (0,
|
|
132
|
+
await (0, import_promises2.mkdir)(import_node_path2.default.dirname(outputFile), { recursive: true });
|
|
114
133
|
const defaultConfig = (0, import_metro_config.getDefaultConfig)(root);
|
|
115
134
|
const babelLocation = require.resolve("@babel/runtime/package");
|
|
116
135
|
const bableNodeModules = await (0, import_find_up.findUp)("node_modules", {
|
|
@@ -130,7 +149,7 @@ async function bundle(root, platform) {
|
|
|
130
149
|
},
|
|
131
150
|
watchFolders: [
|
|
132
151
|
root,
|
|
133
|
-
|
|
152
|
+
import_node_path2.default.resolve(root, "node_modules"),
|
|
134
153
|
bableNodeModules
|
|
135
154
|
],
|
|
136
155
|
reporter: new CustomReporter(),
|
|
@@ -151,30 +170,14 @@ async function bundle(root, platform) {
|
|
|
151
170
|
out: outputFile
|
|
152
171
|
}
|
|
153
172
|
);
|
|
154
|
-
await (0,
|
|
173
|
+
await (0, import_promises2.rename)(
|
|
155
174
|
`${outputFile}.js`,
|
|
156
|
-
|
|
175
|
+
import_node_path2.default.join(root, "build", "output", platform, "main_js_bundle.hbc")
|
|
157
176
|
);
|
|
158
177
|
console.log(` \u2714\uFE0E [${platform}] bundle created`);
|
|
159
178
|
}
|
|
160
|
-
async function getSupportedAppViewTypes(root) {
|
|
161
|
-
const views = await (0, import_promises.readdir)(import_node_path.default.join(root, "src", "views"), {
|
|
162
|
-
withFileTypes: true,
|
|
163
|
-
recursive: false
|
|
164
|
-
});
|
|
165
|
-
const files = views.filter((file) => file.isFile()).map((file) => file.name.split(".")[0]).filter((file) => !!file);
|
|
166
|
-
const validViews = files.filter(
|
|
167
|
-
(file) => VALID_VIEW_TYPES.includes(file)
|
|
168
|
-
);
|
|
169
|
-
if (validViews.length === 0) {
|
|
170
|
-
throw new Error(
|
|
171
|
-
`No valid views found, please create a view in the src/views folder and name it with a valid view type: ${VALID_VIEW_TYPES.join(", ")}`
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
return validViews;
|
|
175
|
-
}
|
|
176
179
|
async function makeEntrypoint(root, platform) {
|
|
177
|
-
const entrypoint =
|
|
180
|
+
const entrypoint = import_node_path2.default.join(
|
|
178
181
|
root,
|
|
179
182
|
"build",
|
|
180
183
|
"entrypoints",
|
|
@@ -194,16 +197,16 @@ ${imports.join("\n")}
|
|
|
194
197
|
|
|
195
198
|
${registry.join("\n")}
|
|
196
199
|
`;
|
|
197
|
-
const entrypointDir =
|
|
198
|
-
await (0,
|
|
199
|
-
await (0,
|
|
200
|
+
const entrypointDir = import_node_path2.default.dirname(entrypoint);
|
|
201
|
+
await (0, import_promises2.mkdir)(entrypointDir, { recursive: true });
|
|
202
|
+
await (0, import_promises2.writeFile)(entrypoint, entrypointContent, "utf-8");
|
|
200
203
|
console.log(` \u2714\uFE0E [${platform}] entrypoint created`);
|
|
201
204
|
return entrypoint;
|
|
202
205
|
}
|
|
203
206
|
async function createMobileBuild(root, platform) {
|
|
204
207
|
const viewTypes = await getSupportedAppViewTypes(root);
|
|
205
|
-
const fullDirectory =
|
|
206
|
-
const mainJsBundle =
|
|
208
|
+
const fullDirectory = import_node_path2.default.join(root, "build", "output", platform);
|
|
209
|
+
const mainJsBundle = import_node_path2.default.join(fullDirectory, "main_js_bundle.hbc");
|
|
207
210
|
if (!(0, import_node_fs.existsSync)(mainJsBundle)) {
|
|
208
211
|
throw new Error(`main_js_bundle.hbc not found in ${fullDirectory}`);
|
|
209
212
|
}
|
|
@@ -226,7 +229,7 @@ async function createMobileBuild(root, platform) {
|
|
|
226
229
|
console.log(
|
|
227
230
|
` \u2714\uFE0E [${platform}] uploaded build: ${fileName} (${(zipData.length / 1024).toFixed(0)} KB)`
|
|
228
231
|
);
|
|
229
|
-
const
|
|
232
|
+
const build2 = await whopSdk.apps.createAppBuild({
|
|
230
233
|
attachment: { directUploadId: uploadedFile.directUploadId },
|
|
231
234
|
checksum,
|
|
232
235
|
platform,
|
|
@@ -237,25 +240,25 @@ async function createMobileBuild(root, platform) {
|
|
|
237
240
|
})[view]
|
|
238
241
|
)
|
|
239
242
|
});
|
|
240
|
-
if (!
|
|
243
|
+
if (!build2) {
|
|
241
244
|
throw new Error("Failed to create app build");
|
|
242
245
|
}
|
|
243
|
-
const dashboardUrl = `https://whop.com/dashboard/${COMPANY_ID}/developer/apps/${APP_ID}/builds
|
|
246
|
+
const dashboardUrl = `https://whop.com/dashboard/${COMPANY_ID}/developer/apps/${APP_ID}/builds/?platform=${platform}`;
|
|
244
247
|
console.log(`
|
|
245
248
|
\u2714\uFE0E [${platform}] deployed as development build \u2714\uFE0E
|
|
246
|
-
- build id: ${
|
|
249
|
+
- build id: ${build2.id}
|
|
247
250
|
- view types: ${viewTypes.join(", ")}
|
|
248
251
|
- promote to production here: ${dashboardUrl}
|
|
249
252
|
`);
|
|
250
|
-
return
|
|
253
|
+
return build2;
|
|
251
254
|
}
|
|
252
255
|
async function zipDirectory(directory) {
|
|
253
256
|
const zip = new import_jszip.default();
|
|
254
257
|
function addFilesToZip(currentPath, relativePath = "") {
|
|
255
258
|
const items = (0, import_node_fs.readdirSync)(currentPath);
|
|
256
259
|
for (const item of items) {
|
|
257
|
-
const fullPath =
|
|
258
|
-
const zipPath = relativePath ?
|
|
260
|
+
const fullPath = import_node_path2.default.join(currentPath, item);
|
|
261
|
+
const zipPath = relativePath ? import_node_path2.default.join(relativePath, item) : item;
|
|
259
262
|
const stats = (0, import_node_fs.statSync)(fullPath);
|
|
260
263
|
if (stats.isDirectory()) {
|
|
261
264
|
addFilesToZip(fullPath, zipPath);
|
|
@@ -274,6 +277,317 @@ var CustomReporter = class {
|
|
|
274
277
|
}
|
|
275
278
|
};
|
|
276
279
|
|
|
280
|
+
// src/cli/web.ts
|
|
281
|
+
var import_promises3 = require("fs/promises");
|
|
282
|
+
var import_node_path3 = __toESM(require("path"));
|
|
283
|
+
var import_esbuild = require("esbuild");
|
|
284
|
+
|
|
285
|
+
// src/cli/reanimated-bable.ts
|
|
286
|
+
var fs = __toESM(require("fs/promises"));
|
|
287
|
+
var path3 = __toESM(require("path"));
|
|
288
|
+
var babel = __toESM(require("@babel/core"));
|
|
289
|
+
var JS_RE = /\.(m|c)?(t|j)sx?$/;
|
|
290
|
+
function reanimatedBabelPlugin() {
|
|
291
|
+
const shouldTransform = (p) => {
|
|
292
|
+
if (!JS_RE.test(p)) return false;
|
|
293
|
+
if (p.includes(`${path3.sep}react-native-reanimated${path3.sep}`))
|
|
294
|
+
return true;
|
|
295
|
+
if (p.includes(`${path3.sep}node_modules${path3.sep}`)) return false;
|
|
296
|
+
return p.includes(`${path3.sep}src${path3.sep}`);
|
|
297
|
+
};
|
|
298
|
+
return {
|
|
299
|
+
name: "reanimated-babel",
|
|
300
|
+
setup(b) {
|
|
301
|
+
b.onLoad({ filter: JS_RE }, async (args) => {
|
|
302
|
+
if (!shouldTransform(args.path)) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
const code = await fs.readFile(args.path, "utf8");
|
|
306
|
+
const result = await babel.transformAsync(code, {
|
|
307
|
+
filename: args.path,
|
|
308
|
+
sourceMaps: false,
|
|
309
|
+
babelrc: false,
|
|
310
|
+
configFile: false,
|
|
311
|
+
// ORDER MATTERS: Reanimated plugin MUST BE LAST
|
|
312
|
+
plugins: [
|
|
313
|
+
// Needed by Reanimated on web per docs
|
|
314
|
+
"@babel/plugin-transform-export-namespace-from",
|
|
315
|
+
// Handle Flow types present in some RN libs
|
|
316
|
+
[
|
|
317
|
+
"@babel/plugin-transform-flow-strip-types",
|
|
318
|
+
{ allowDeclareFields: true }
|
|
319
|
+
],
|
|
320
|
+
// MUST be last
|
|
321
|
+
[
|
|
322
|
+
"react-native-reanimated/plugin",
|
|
323
|
+
{ relativeSourceLocation: true }
|
|
324
|
+
]
|
|
325
|
+
],
|
|
326
|
+
presets: [],
|
|
327
|
+
// esbuild handles TS/JSX syntax; no preset-env/preset-react
|
|
328
|
+
caller: { name: "esbuild" },
|
|
329
|
+
// Let Babel parse TS/JSX/Flow; keep it broad
|
|
330
|
+
parserOpts: { plugins: ["jsx", "typescript"] },
|
|
331
|
+
generatorOpts: { decoratorsBeforeExport: true }
|
|
332
|
+
});
|
|
333
|
+
return {
|
|
334
|
+
// biome-ignore lint/style/noNonNullAssertion: <explanation>
|
|
335
|
+
contents: result.code,
|
|
336
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
337
|
+
loader: pickLoader(args.path)
|
|
338
|
+
};
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function pickLoader(file) {
|
|
344
|
+
const ext = path3.extname(file).toLowerCase();
|
|
345
|
+
if (ext === ".tsx") return "tsx";
|
|
346
|
+
if (ext === ".ts") return "ts";
|
|
347
|
+
if (ext === ".jsx") return "jsx";
|
|
348
|
+
return "jsx";
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// src/cli/strip-flow.ts
|
|
352
|
+
var fs2 = __toESM(require("fs/promises"));
|
|
353
|
+
var babel2 = __toESM(require("@babel/core"));
|
|
354
|
+
function stripFlowWithBabel() {
|
|
355
|
+
const filter = /\.(m|c)?jsx?$/;
|
|
356
|
+
return {
|
|
357
|
+
name: "strip-flow-with-babel",
|
|
358
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
359
|
+
setup(b) {
|
|
360
|
+
b.onLoad({ filter }, async (args) => {
|
|
361
|
+
const code = await fs2.readFile(args.path, "utf8");
|
|
362
|
+
const out = await babel2.transformAsync(code, {
|
|
363
|
+
filename: args.path,
|
|
364
|
+
babelrc: false,
|
|
365
|
+
configFile: false,
|
|
366
|
+
plugins: [
|
|
367
|
+
[
|
|
368
|
+
"@babel/plugin-transform-flow-strip-types",
|
|
369
|
+
{ allowDeclareFields: true }
|
|
370
|
+
]
|
|
371
|
+
],
|
|
372
|
+
parserOpts: { plugins: ["jsx", "flow"] },
|
|
373
|
+
sourceMaps: false
|
|
374
|
+
});
|
|
375
|
+
return { contents: out.code, loader: "jsx" };
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/cli/web.ts
|
|
382
|
+
function aliasReactNativePlugin() {
|
|
383
|
+
return {
|
|
384
|
+
name: "alias-react-native",
|
|
385
|
+
setup(b) {
|
|
386
|
+
b.onResolve({ filter: /^react-native$/ }, () => ({
|
|
387
|
+
path: require.resolve("react-native-web")
|
|
388
|
+
}));
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function forceSingleReact() {
|
|
393
|
+
const map = /* @__PURE__ */ new Map([
|
|
394
|
+
["react", require.resolve("react")],
|
|
395
|
+
["react/jsx-runtime", require.resolve("react/jsx-runtime")],
|
|
396
|
+
["react/jsx-dev-runtime", require.resolve("react/jsx-dev-runtime")],
|
|
397
|
+
["react-dom", require.resolve("react-dom")],
|
|
398
|
+
["react-dom/client", require.resolve("react-dom/client")]
|
|
399
|
+
]);
|
|
400
|
+
const rx = /^(react(?:\/jsx-(?:dev-)?runtime)?|react-dom(?:\/client)?)$/;
|
|
401
|
+
return {
|
|
402
|
+
name: "force-single-react",
|
|
403
|
+
setup(b) {
|
|
404
|
+
b.onResolve({ filter: rx }, (args) => ({ path: map.get(args.path) }));
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function toPascalCase(str) {
|
|
409
|
+
return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
410
|
+
}
|
|
411
|
+
async function makeWebEntrypoint(root) {
|
|
412
|
+
const files = await getSupportedAppViewTypes(root);
|
|
413
|
+
const packageJsonPath = import_node_path3.default.join(root, "package.json");
|
|
414
|
+
const packageJson = JSON.parse(await (0, import_promises3.readFile)(packageJsonPath, "utf-8"));
|
|
415
|
+
const hasReactNativeReanimated = packageJson.dependencies?.["react-native-reanimated"];
|
|
416
|
+
const imports = files.map(
|
|
417
|
+
(file) => `import { ${toPascalCase(file)} } from "../../../src/views/${file}";`
|
|
418
|
+
);
|
|
419
|
+
const registry = files.map(
|
|
420
|
+
(file) => `AppRegistry.registerComponent("${toPascalCase(file)}", () => WhopNavigationWrapper(React, "${toPascalCase(file)}", ${toPascalCase(file)}));`
|
|
421
|
+
);
|
|
422
|
+
const defaultKey = toPascalCase(files[0] ?? "experience-view");
|
|
423
|
+
const reanimatedImport = hasReactNativeReanimated ? `import "react-native-reanimated";` : "";
|
|
424
|
+
const entry = `import { AppRegistry } from "react-native";
|
|
425
|
+
import * as React from "react";
|
|
426
|
+
import { WhopNavigationWrapper } from "@whop/react-native/web";
|
|
427
|
+
${reanimatedImport}
|
|
428
|
+
|
|
429
|
+
${imports.join("\n")}
|
|
430
|
+
|
|
431
|
+
${registry.join("\n")}
|
|
432
|
+
|
|
433
|
+
const root = document.getElementById("root") || (() => {
|
|
434
|
+
const d = document.createElement("div");
|
|
435
|
+
d.id = "root";
|
|
436
|
+
document.body.appendChild(d);
|
|
437
|
+
return d;
|
|
438
|
+
})();
|
|
439
|
+
AppRegistry.runApplication("${defaultKey}", { rootTag: root });
|
|
440
|
+
`;
|
|
441
|
+
const entryFile = import_node_path3.default.join(root, "build", "entrypoints", "web", "index.tsx");
|
|
442
|
+
await (0, import_promises3.mkdir)(import_node_path3.default.dirname(entryFile), { recursive: true });
|
|
443
|
+
await (0, import_promises3.writeFile)(entryFile, entry, "utf-8");
|
|
444
|
+
return entryFile;
|
|
445
|
+
}
|
|
446
|
+
async function bundleWeb(root) {
|
|
447
|
+
const entry = await makeWebEntrypoint(root);
|
|
448
|
+
const outDir = import_node_path3.default.join(root, "build", "output", "web");
|
|
449
|
+
await (0, import_promises3.mkdir)(outDir, { recursive: true });
|
|
450
|
+
await (0, import_esbuild.build)({
|
|
451
|
+
entryPoints: [entry],
|
|
452
|
+
outfile: import_node_path3.default.join(outDir, "main.js"),
|
|
453
|
+
bundle: true,
|
|
454
|
+
minify: false,
|
|
455
|
+
format: "esm",
|
|
456
|
+
platform: "browser",
|
|
457
|
+
sourcemap: false,
|
|
458
|
+
jsx: "automatic",
|
|
459
|
+
mainFields: ["browser", "module", "main"],
|
|
460
|
+
conditions: ["browser", "import", "default"],
|
|
461
|
+
define: {
|
|
462
|
+
process: "{}",
|
|
463
|
+
"process.env": "{}",
|
|
464
|
+
"process.env.NODE_ENV": '"production"',
|
|
465
|
+
__DEV__: "false",
|
|
466
|
+
"process.env.NEXT_PUBLIC_WHOP_APP_ID": `"${APP_ID}"`,
|
|
467
|
+
// Some RN libraries (e.g., RNGH) expect a Node-like global in the browser
|
|
468
|
+
global: "globalThis"
|
|
469
|
+
},
|
|
470
|
+
resolveExtensions: [
|
|
471
|
+
".web.tsx",
|
|
472
|
+
".web.ts",
|
|
473
|
+
".web.js",
|
|
474
|
+
".tsx",
|
|
475
|
+
".ts",
|
|
476
|
+
".jsx",
|
|
477
|
+
".js"
|
|
478
|
+
],
|
|
479
|
+
loader: {
|
|
480
|
+
".png": "dataurl",
|
|
481
|
+
".jpg": "dataurl",
|
|
482
|
+
".jpeg": "dataurl",
|
|
483
|
+
".svg": "dataurl",
|
|
484
|
+
".ttf": "dataurl",
|
|
485
|
+
".woff": "dataurl",
|
|
486
|
+
".woff2": "dataurl",
|
|
487
|
+
".js": "jsx",
|
|
488
|
+
".jsx": "jsx"
|
|
489
|
+
},
|
|
490
|
+
plugins: [
|
|
491
|
+
forceSingleReact(),
|
|
492
|
+
aliasReactNativePlugin(),
|
|
493
|
+
reanimatedBabelPlugin(),
|
|
494
|
+
stripFlowWithBabel(),
|
|
495
|
+
{
|
|
496
|
+
name: "force-native-web-stub",
|
|
497
|
+
setup(b) {
|
|
498
|
+
b.onResolve({ filter: /^\.\/native-whop-core$/ }, (args) => {
|
|
499
|
+
return {
|
|
500
|
+
path: import_node_path3.default.join(args.resolveDir, "native-whop-core"),
|
|
501
|
+
namespace: "file"
|
|
502
|
+
};
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
]
|
|
507
|
+
});
|
|
508
|
+
const html = `<!doctype html>
|
|
509
|
+
<html>
|
|
510
|
+
<head>
|
|
511
|
+
<meta charset="utf-8" />
|
|
512
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
513
|
+
<title>Whop App (Web)</title>
|
|
514
|
+
<style>
|
|
515
|
+
#root {
|
|
516
|
+
width: 100vw;
|
|
517
|
+
height: 100vh;
|
|
518
|
+
margin: 0;
|
|
519
|
+
padding: 0;
|
|
520
|
+
overflow: hidden;
|
|
521
|
+
display: flex;
|
|
522
|
+
flex-direction: column;
|
|
523
|
+
align-items: stretch;
|
|
524
|
+
justify-content: start;
|
|
525
|
+
}
|
|
526
|
+
</style>
|
|
527
|
+
</head>
|
|
528
|
+
<body>
|
|
529
|
+
<div id="root"></div>
|
|
530
|
+
<script type="module" src="./main.js"></script>
|
|
531
|
+
</body>
|
|
532
|
+
</html>`;
|
|
533
|
+
await (0, import_promises3.writeFile)(import_node_path3.default.join(outDir, "index.html"), html, "utf-8");
|
|
534
|
+
console.log(" \u2714\uFE0E [web] bundle created at build/output/web/main.js");
|
|
535
|
+
}
|
|
536
|
+
async function buildAndPublish2(root, {
|
|
537
|
+
shouldBuild = true,
|
|
538
|
+
shouldUpload = true
|
|
539
|
+
} = {
|
|
540
|
+
shouldBuild: true,
|
|
541
|
+
shouldUpload: true
|
|
542
|
+
}) {
|
|
543
|
+
if (shouldBuild) {
|
|
544
|
+
await bundleWeb(root);
|
|
545
|
+
}
|
|
546
|
+
if (shouldUpload) {
|
|
547
|
+
await createWebBuild(root);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
async function createWebBuild(root) {
|
|
551
|
+
const fullDirectory = import_node_path3.default.join(root, "build", "output", "web");
|
|
552
|
+
const mainJsFile = import_node_path3.default.join(fullDirectory, "main.js");
|
|
553
|
+
try {
|
|
554
|
+
await (0, import_promises3.readFile)(mainJsFile);
|
|
555
|
+
} catch {
|
|
556
|
+
throw new Error(`main.js not found in ${fullDirectory}`);
|
|
557
|
+
}
|
|
558
|
+
const buf = await (0, import_promises3.readFile)(mainJsFile);
|
|
559
|
+
const checksum = await getChecksum(buf);
|
|
560
|
+
console.log(` \u2714\uFE0E [web] build checksummed: ${checksum}`);
|
|
561
|
+
const fileName = `rnweb_${checksum}.js`;
|
|
562
|
+
const uploadedFile = await uploadFile(
|
|
563
|
+
buf,
|
|
564
|
+
fileName,
|
|
565
|
+
"application/javascript"
|
|
566
|
+
);
|
|
567
|
+
console.log(
|
|
568
|
+
` \u2714\uFE0E [web] uploaded build: ${fileName} (${(buf.length / 1024).toFixed(0)} KB)`
|
|
569
|
+
);
|
|
570
|
+
const build2 = await whopSdk.apps.createAppBuild({
|
|
571
|
+
attachment: { directUploadId: uploadedFile.directUploadId },
|
|
572
|
+
checksum,
|
|
573
|
+
platform: "web",
|
|
574
|
+
supportedAppViewTypes: ["hub"]
|
|
575
|
+
});
|
|
576
|
+
if (!build2) {
|
|
577
|
+
throw new Error("Failed to create app build");
|
|
578
|
+
}
|
|
579
|
+
const dashboardUrl = `https://whop.com/dashboard/${COMPANY_ID}/developer/apps/${APP_ID}/builds/?platform=web`;
|
|
580
|
+
console.log(
|
|
581
|
+
`
|
|
582
|
+
\u2714\uFE0E [web] deployed as development build \u2714\uFE0E
|
|
583
|
+
- build id: ${build2.id}
|
|
584
|
+
- view types: hub
|
|
585
|
+
- promote to production here: ${dashboardUrl}
|
|
586
|
+
`
|
|
587
|
+
);
|
|
588
|
+
return build2;
|
|
589
|
+
}
|
|
590
|
+
|
|
277
591
|
// src/cli/index.ts
|
|
278
592
|
async function main() {
|
|
279
593
|
const args = (0, import_node_util.parseArgs)({
|
|
@@ -293,6 +607,10 @@ async function main() {
|
|
|
293
607
|
args: process.argv.slice(2)
|
|
294
608
|
});
|
|
295
609
|
const [command] = args.positionals;
|
|
610
|
+
if (command === "install") {
|
|
611
|
+
await handleInstall();
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
296
614
|
let shouldBuild = true;
|
|
297
615
|
let shouldUpload = true;
|
|
298
616
|
let shouldClean = true;
|
|
@@ -331,12 +649,12 @@ async function main() {
|
|
|
331
649
|
promises.push(buildAndPublish(root, "android", opts));
|
|
332
650
|
}
|
|
333
651
|
if (args.values.web || !didProvidePlatform) {
|
|
334
|
-
|
|
652
|
+
promises.push(buildAndPublish2(root, opts));
|
|
335
653
|
}
|
|
336
654
|
await Promise.all(promises);
|
|
337
655
|
}
|
|
338
656
|
async function cleanBuildDirectory(root) {
|
|
339
|
-
const buildDirectory =
|
|
657
|
+
const buildDirectory = import_node_path4.default.join(root, "build");
|
|
340
658
|
if ((0, import_node_fs2.existsSync)(buildDirectory)) {
|
|
341
659
|
await (0, import_rimraf.rimraf)(buildDirectory);
|
|
342
660
|
}
|
|
@@ -349,9 +667,20 @@ async function getRootProjectDirectory() {
|
|
|
349
667
|
"please run this command inside a whop react native project"
|
|
350
668
|
);
|
|
351
669
|
}
|
|
352
|
-
const root =
|
|
670
|
+
const root = import_node_path4.default.dirname(file);
|
|
353
671
|
return root;
|
|
354
672
|
}
|
|
673
|
+
async function handleInstall() {
|
|
674
|
+
const appId = env("NEXT_PUBLIC_WHOP_APP_ID");
|
|
675
|
+
const installLink = `https://whop.com/apps/${appId}/install`;
|
|
676
|
+
console.log(`
|
|
677
|
+
Open this link in your browser to install the app into your whop.
|
|
678
|
+
${installLink}
|
|
679
|
+
|
|
680
|
+
Or scan the QR code with your iPhone:
|
|
681
|
+
`);
|
|
682
|
+
import_qrcode_terminal.default.generate(installLink, { small: true });
|
|
683
|
+
}
|
|
355
684
|
main().catch((err) => {
|
|
356
685
|
console.error(err);
|
|
357
686
|
process.exit(1);
|