@calo-design/cli 0.9.0 → 0.9.2
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 +112 -13
- package/package.json +1 -1
package/bin/mirror-push.js
CHANGED
|
@@ -293,21 +293,75 @@ function linkNodeModules(stage) {
|
|
|
293
293
|
fs.symlinkSync(target, link, process.platform === "win32" ? "junction" : "dir");
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
//
|
|
296
|
+
// Wrap the root _layout with Mirror chrome. Returning to the launcher is
|
|
297
297
|
// handled natively by the Mirror shell (shake the device — see withCaloMirrorIos),
|
|
298
298
|
// so no visible "back" control is injected; MirrorChrome carries the injected
|
|
299
|
-
// crash reporting (Sentry init + error boundary).
|
|
300
|
-
//
|
|
299
|
+
// crash reporting (Sentry init + error boundary).
|
|
300
|
+
//
|
|
301
|
+
// The author's own root _layout is PRESERVED: it's relocated one directory up
|
|
302
|
+
// (next to mirror-chrome, where expo-router won't treat it as a route — inside
|
|
303
|
+
// the app dir even underscore-prefixed files become routes), its relative
|
|
304
|
+
// imports are shifted for the move, and the managed layout renders it inside
|
|
305
|
+
// MirrorChrome. Providers, fonts, and custom UI in the root layout all carry
|
|
306
|
+
// over — the old behavior silently dropped them, and every context hook in the
|
|
307
|
+
// prototype then threw "must be used inside <Provider>" at runtime.
|
|
308
|
+
|
|
309
|
+
// Moving a module one directory up (src/app/_layout → src/mirror-original-layout)
|
|
310
|
+
// shifts what its relative imports resolve to; rewrite the specifiers so they
|
|
311
|
+
// point at the same files from the new location. Covers `from "…"`, bare
|
|
312
|
+
// `import "…"`, dynamic `import("…")` and `require("…")`. Alias (@/…) and
|
|
313
|
+
// package imports are untouched.
|
|
314
|
+
function shiftRelativeImportsUp(source, appDirName) {
|
|
315
|
+
return source.replace(
|
|
316
|
+
/(\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)(["'])(\.{1,2}\/[^"']+)\2/g,
|
|
317
|
+
(whole, lead, quote, spec) => {
|
|
318
|
+
let next;
|
|
319
|
+
if (spec.startsWith("../")) {
|
|
320
|
+
next = spec.slice(3);
|
|
321
|
+
if (!next.startsWith("../") && !next.startsWith("./")) next = "./" + next;
|
|
322
|
+
} else {
|
|
323
|
+
next = "./" + appDirName + "/" + spec.slice(2);
|
|
324
|
+
}
|
|
325
|
+
return `${lead}${quote}${next}${quote}`;
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
301
330
|
function injectBackChrome({ stagedAppDir, mirrorChromeDir, libRel, slug, owner }) {
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
331
|
+
const exts = [".tsx", ".jsx", ".ts", ".js"];
|
|
332
|
+
const ext = exts.find((e) => fs.existsSync(path.join(stagedAppDir, "_layout" + e)));
|
|
333
|
+
const authorLayout = ext ? fs.readFileSync(path.join(stagedAppDir, "_layout" + ext), "utf8") : null;
|
|
334
|
+
// Root layouts must default-export a component; without one we can't wrap it.
|
|
335
|
+
const wrappable = authorLayout && /export\s+default\b|export\s*\{[^}]*\bdefault\b[^}]*\}/.test(authorLayout);
|
|
336
|
+
|
|
337
|
+
let managed;
|
|
338
|
+
if (wrappable) {
|
|
339
|
+
fs.writeFileSync(
|
|
340
|
+
path.join(mirrorChromeDir, "mirror-original-layout" + ext),
|
|
341
|
+
shiftRelativeImportsUp(authorLayout, path.basename(stagedAppDir))
|
|
342
|
+
);
|
|
343
|
+
fs.rmSync(path.join(stagedAppDir, "_layout" + ext));
|
|
344
|
+
log(c.dim(" root _layout preserved — wrapped with Mirror crash reporting (providers carry over)"));
|
|
345
|
+
managed = `// Managed by calo-design push. Your root _layout is preserved verbatim as
|
|
346
|
+
// ../mirror-original-layout (imports shifted for the move) and rendered inside
|
|
347
|
+
// Mirror's crash reporting — providers, fonts, and custom UI all carry over.
|
|
348
|
+
// MirrorChrome MUST stay the first import: its module scope boots Sentry, so a
|
|
349
|
+
// crash while any later module evaluates (your layout, the design system — the
|
|
350
|
+
// SDK-drift class) is captured instead of dying unreported before init.
|
|
351
|
+
import { MirrorChrome } from "${libRel}";
|
|
352
|
+
import OriginalLayout from "../mirror-original-layout";
|
|
353
|
+
|
|
354
|
+
export default function RootLayout() {
|
|
355
|
+
return (
|
|
356
|
+
<MirrorChrome>
|
|
357
|
+
<OriginalLayout />
|
|
358
|
+
</MirrorChrome>
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
`;
|
|
362
|
+
} else {
|
|
363
|
+
if (authorLayout) warn("root _layout has no default export — replacing it with the standard Mirror layout.");
|
|
364
|
+
managed = `// MirrorChrome MUST be the first import: its module scope boots Sentry, so a
|
|
311
365
|
// crash while any later module evaluates (expo-router, the design system — the
|
|
312
366
|
// SDK-drift class) is captured instead of dying unreported before init.
|
|
313
367
|
import { MirrorChrome } from "${libRel}";
|
|
@@ -324,9 +378,54 @@ export default function RootLayout() {
|
|
|
324
378
|
</MirrorChrome>
|
|
325
379
|
);
|
|
326
380
|
}
|
|
381
|
+
`;
|
|
382
|
+
}
|
|
383
|
+
fs.writeFileSync(path.join(stagedAppDir, "_layout.tsx"), managed);
|
|
384
|
+
fs.writeFileSync(path.join(mirrorChromeDir, "mirror-chrome.tsx"), mirrorChromeSource({ slug, owner }));
|
|
385
|
+
injectNativeIntent(stagedAppDir);
|
|
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) {
|
|
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. If opening by QR/link lands on \"Unmatched Route\", redirect paths starting with /open to \"/\".");
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
fs.writeFileSync(
|
|
406
|
+
path.join(stagedAppDir, "+native-intent.tsx"),
|
|
407
|
+
`// Managed by calo-design push.
|
|
408
|
+
// The Mirror launcher opens this prototype with designchef://open/<slug>. That URL
|
|
409
|
+
// is still the native initial URL after the reload into this bundle, where /open/*
|
|
410
|
+
// is not a route — without this hook expo-router renders "Unmatched Route".
|
|
411
|
+
const LAUNCHER_PATH = /^\\/open(\\/|\$)/;
|
|
412
|
+
|
|
413
|
+
export function redirectSystemPath({ path }: { path: string; initial: boolean }): string {
|
|
414
|
+
try {
|
|
415
|
+
// Bare pathname, e.g. "/open/<slug>".
|
|
416
|
+
if (path.startsWith("/")) return LAUNCHER_PATH.test(path) ? "/" : path;
|
|
417
|
+
const url = new URL(path);
|
|
418
|
+
// Both shapes reach us: "designchef://open/<slug>" parses with host "open" and
|
|
419
|
+
// pathname "/<slug>", while "designchef:///open/<slug>" has an empty host and
|
|
420
|
+
// pathname "/open/<slug>". Treat either as the launcher handing us this app.
|
|
421
|
+
if (url.host === "open" || LAUNCHER_PATH.test(url.pathname)) return "/";
|
|
422
|
+
} catch {
|
|
423
|
+
// Unparseable URL — hand it back untouched rather than swallowing the link.
|
|
424
|
+
}
|
|
425
|
+
return path;
|
|
426
|
+
}
|
|
327
427
|
`
|
|
328
428
|
);
|
|
329
|
-
fs.writeFileSync(path.join(mirrorChromeDir, "mirror-chrome.tsx"), mirrorChromeSource({ slug, owner }));
|
|
330
429
|
}
|
|
331
430
|
|
|
332
431
|
// 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.2",
|
|
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"
|