@calo-design/cli 0.9.1 → 0.9.3
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/bin/mirror-push.js +92 -0
- package/package.json +1 -1
package/bin/mirror-push.js
CHANGED
|
@@ -382,6 +382,98 @@ export default function RootLayout() {
|
|
|
382
382
|
}
|
|
383
383
|
fs.writeFileSync(path.join(stagedAppDir, "_layout.tsx"), managed);
|
|
384
384
|
fs.writeFileSync(path.join(mirrorChromeDir, "mirror-chrome.tsx"), mirrorChromeSource({ slug, owner }));
|
|
385
|
+
injectNativeIntent(stagedAppDir, slug);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// The launcher opens a prototype via `designchef://open/<slug>`. That deep link is
|
|
389
|
+
// the app's native initial URL, and it SURVIVES the expo-updates reload into the
|
|
390
|
+
// prototype's bundle — where `/open/<slug>` isn't a route, so expo-router shows its
|
|
391
|
+
// "Unmatched Route" screen instead of the prototype. (Feed taps don't carry a URL,
|
|
392
|
+
// which is why only QR/link opens were affected.)
|
|
393
|
+
//
|
|
394
|
+
// `+native-intent.tsx` is expo-router's hook for rewriting an incoming native URL
|
|
395
|
+
// before routing: send the launcher's own path to the root and leave every other
|
|
396
|
+
// deep link untouched, so a prototype's own links keep working.
|
|
397
|
+
function injectNativeIntent(stagedAppDir, slug) {
|
|
398
|
+
const existing = [".tsx", ".ts", ".jsx", ".js"].find((e) =>
|
|
399
|
+
fs.existsSync(path.join(stagedAppDir, "+native-intent" + e))
|
|
400
|
+
);
|
|
401
|
+
if (existing) {
|
|
402
|
+
warn("this prototype has its own +native-intent — leaving it alone. Launcher URLs (/open/<slug>) won't be handled: route them to \"/\" for this prototype's slug and switch expo-updates channels for other slugs.");
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
fs.writeFileSync(
|
|
406
|
+
path.join(stagedAppDir, "+native-intent.tsx"),
|
|
407
|
+
`// Managed by calo-design push.
|
|
408
|
+
// The Mirror launcher opens prototypes with designchef://open/<slug>. Two cases
|
|
409
|
+
// reach THIS bundle:
|
|
410
|
+
// - the URL that opened this prototype survives the expo-updates reload as the
|
|
411
|
+
// initial URL (/open/<own slug>) — route it to "/" so expo-router doesn't
|
|
412
|
+
// render "Unmatched Route";
|
|
413
|
+
// - a designchef://open/<other> link arrives while this bundle is live — the
|
|
414
|
+
// launcher's JS isn't running, so this bundle performs the channel switch
|
|
415
|
+
// itself: point expo-updates at the other channel, download, reload.
|
|
416
|
+
// ("mirror" is the launcher's own channel, so open/mirror = back to the feed.)
|
|
417
|
+
import { Platform } from "react-native";
|
|
418
|
+
import * as Updates from "expo-updates";
|
|
419
|
+
|
|
420
|
+
const PROTOTYPE = ${JSON.stringify(slug)};
|
|
421
|
+
const UPDATES_URL = ${JSON.stringify(UPDATES_URL)};
|
|
422
|
+
const RUNTIME_VERSION = ${JSON.stringify(RUNTIME_VERSION)};
|
|
423
|
+
|
|
424
|
+
// One switch at a time — a second reload racing the first crashes in JSI teardown
|
|
425
|
+
// (see calo-design-mirror src/lib/mirror.ts). Reset on failure so retry works.
|
|
426
|
+
let switching = false;
|
|
427
|
+
function switchToChannel(channel: string): void {
|
|
428
|
+
if (switching) return;
|
|
429
|
+
let enabled = false;
|
|
430
|
+
try { enabled = Updates.isEnabled; } catch {}
|
|
431
|
+
if (!enabled) return; // dev/Expo Go — no runtime loading there
|
|
432
|
+
switching = true;
|
|
433
|
+
(async () => {
|
|
434
|
+
await Updates.setUpdateURLAndRequestHeadersOverride({
|
|
435
|
+
updateUrl: UPDATES_URL,
|
|
436
|
+
// EAS requires all three headers — channel alone is an HTTP 400.
|
|
437
|
+
requestHeaders: {
|
|
438
|
+
"expo-channel-name": channel,
|
|
439
|
+
"expo-runtime-version": Updates.runtimeVersion ?? RUNTIME_VERSION,
|
|
440
|
+
"expo-platform": Platform.OS,
|
|
441
|
+
},
|
|
442
|
+
});
|
|
443
|
+
// Download BEFORE reloading; if the channel has nothing this throws and we
|
|
444
|
+
// stay put instead of reloading into nothing.
|
|
445
|
+
await Updates.fetchUpdateAsync();
|
|
446
|
+
// Defer the reload off the awaited chain (JSI teardown race — see mirror.ts).
|
|
447
|
+
setTimeout(() => { void Updates.reloadAsync().catch(() => { switching = false; }); }, 300);
|
|
448
|
+
})().catch(() => { switching = false; });
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// "/open/<slug>", "scheme://open/<slug>" (host parses as "open"), and
|
|
452
|
+
// "scheme:///open/<slug>" all mean the launcher; anything else is the
|
|
453
|
+
// prototype's own link. Returns the slug ("" for a bare /open), or null.
|
|
454
|
+
function launcherSlug(path: string): string | null {
|
|
455
|
+
try {
|
|
456
|
+
let pathname: string;
|
|
457
|
+
if (path.startsWith("/")) pathname = path;
|
|
458
|
+
else {
|
|
459
|
+
const url = new URL(path);
|
|
460
|
+
pathname = url.host === "open" ? "/open" + url.pathname : url.pathname;
|
|
461
|
+
}
|
|
462
|
+
const m = pathname.match(/^\\/open(?:\\/([^/?#]*))?(?:[/?#]|$)/);
|
|
463
|
+
return m ? decodeURIComponent(m[1] ?? "") : null;
|
|
464
|
+
} catch {
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export function redirectSystemPath({ path }: { path: string; initial: boolean }): string {
|
|
470
|
+
const slug = launcherSlug(path);
|
|
471
|
+
if (slug === null) return path; // not a launcher URL — leave this prototype's own links alone
|
|
472
|
+
if (slug && slug !== PROTOTYPE) switchToChannel(slug);
|
|
473
|
+
return "/";
|
|
474
|
+
}
|
|
475
|
+
`
|
|
476
|
+
);
|
|
385
477
|
}
|
|
386
478
|
|
|
387
479
|
// True when the shared runtime can bundle @sentry/react-native. Old runtimes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calo-design/cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"description": "One-line setup for Calo design tooling: logs in with your Calo email and installs the calo-design skill + design-system packages. No GitHub account needed.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"calo-design": "bin/cli.js"
|