@gfxlabs/third-eye-cli 3.25.0 → 3.25.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/dist/bin.mjs CHANGED
@@ -27,6 +27,16 @@ import { RPCLink } from "@orpc/client/fetch";
27
27
  import { execa } from "execa";
28
28
  import { XMLParser } from "fast-xml-parser";
29
29
  //#region \0rolldown/runtime.js
30
+ var __defProp = Object.defineProperty;
31
+ var __exportAll = (all, no_symbols) => {
32
+ let target = {};
33
+ for (var name in all) __defProp(target, name, {
34
+ get: all[name],
35
+ enumerable: true
36
+ });
37
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
38
+ return target;
39
+ };
30
40
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
31
41
  //#endregion
32
42
  //#region src/log.ts
@@ -1231,6 +1241,22 @@ const getPagesFromExternalLoader = async () => {
1231
1241
  };
1232
1242
  //#endregion
1233
1243
  //#region src/shots/shots.ts
1244
+ /**
1245
+ * Wait for Storybook play functions to complete before screenshotting.
1246
+ * Checks __STORYBOOK_PREVIEW__.currentRender.phase and data-play-fn-done attribute.
1247
+ */
1248
+ const waitForPlayFunction = async (page, shotItem, logger) => {
1249
+ if (shotItem.shotMode !== "storybook") return;
1250
+ try {
1251
+ await page.waitForFunction(() => {
1252
+ if (window.__STORYBOOK_PREVIEW__?.currentRender?.phase === "playing") return false;
1253
+ if (document.getElementById("storybook-root")?.dataset.playFnDone === "false") return false;
1254
+ return true;
1255
+ }, { timeout: config.timeouts?.loadState ?? 3e4 });
1256
+ } catch {
1257
+ logger.process("info", "general", `Play function wait timed out for ${shotItem.id} — proceeding with screenshot`);
1258
+ }
1259
+ };
1234
1260
  const takeScreenShot = async ({ browser, shotItem, logger }) => {
1235
1261
  const context = await browser.newContext(shotItem.browserConfig);
1236
1262
  const page = await context.newPage();
@@ -1275,6 +1301,7 @@ const takeScreenShot = async ({ browser, shotItem, logger }) => {
1275
1301
  } catch (error) {
1276
1302
  logger.process("error", "timeout", `Timeout while waiting for all network requests: ${shotItem.url}`, error);
1277
1303
  }
1304
+ await waitForPlayFunction(page, shotItem, logger);
1278
1305
  if (config.beforeScreenshot) await config.beforeScreenshot(page, {
1279
1306
  shotMode: shotItem.shotMode,
1280
1307
  id: shotItem.id,
@@ -1702,12 +1729,6 @@ const getParentCommits = async (hasBuildsWithCommits, options) => {
1702
1729
  visitedCommitsWithoutBuilds: commitsWithoutBuilds
1703
1730
  };
1704
1731
  }
1705
- if (commitsWithBuilds.length > 1) {
1706
- const parentArgs = commitsWithBuilds.map((c) => `"${c}^@"`).join(" ");
1707
- const maxOutput = execGit(`git rev-list ${commitsWithBuilds.join(" ")} --not ${parentArgs}`);
1708
- const maxCommits = maxOutput ? maxOutput.split("\n").filter(Boolean) : [];
1709
- if (maxCommits.length > 0) commitsWithBuilds = maxCommits;
1710
- }
1711
1732
  log.process("info", "general", `📌 Found ${commitsWithBuilds.length} ancestor build(s): ${commitsWithBuilds.map((c) => c.slice(0, 7)).join(", ")}`);
1712
1733
  return {
1713
1734
  parentCommits: commitsWithBuilds,
@@ -1773,6 +1794,21 @@ const createTypedClient = (options) => {
1773
1794
  };
1774
1795
  //#endregion
1775
1796
  //#region src/api.ts
1797
+ var api_exports = /* @__PURE__ */ __exportAll({
1798
+ getAffectedStories: () => getAffectedStories,
1799
+ getApiToken: () => getApiToken,
1800
+ getGitInfoFromAPI: () => getGitInfoFromAPI,
1801
+ prepareUpload: () => prepareUpload,
1802
+ processShots: () => processShots,
1803
+ sendCheckCacheToAPI: () => sendCheckCacheToAPI,
1804
+ sendFinalizeToAPI: () => sendFinalizeToAPI,
1805
+ sendFindSquashMergeParentsToAPI: () => sendFindSquashMergeParentsToAPI,
1806
+ sendHasBuildsWithCommitsToAPI: () => sendHasBuildsWithCommitsToAPI,
1807
+ sendInitToAPI: () => sendInitToAPI,
1808
+ sendRecordLogsToAPI: () => sendRecordLogsToAPI,
1809
+ uploadShot: () => uploadShot,
1810
+ uploadStorybookArchive: () => uploadStorybookArchive
1811
+ });
1776
1812
  const createClient = (platformUrl, apiKey, apiToken) => createTypedClient({
1777
1813
  url: platformUrl,
1778
1814
  apiKey,
@@ -2253,6 +2289,16 @@ const platformRunner = async (config, apiToken) => {
2253
2289
  initialCommitsWithBuilds.push(lastCommit);
2254
2290
  } else log.process("info", "general", `📌 Last build on branch ${lastCommit.slice(0, 7)} not in local git (rebase?), skipping`);
2255
2291
  }
2292
+ if (config.baseBranch) {
2293
+ const mergeBase = getMergeBaseInfo(config.baseBranch);
2294
+ if (mergeBase) {
2295
+ const mbCommitsWithBuilds = await sendHasBuildsWithCommitsToAPI(config, apiToken, mergeBase.mergeBaseAncestors);
2296
+ for (const c of mbCommitsWithBuilds) if (!initialCommitsWithBuilds.includes(c)) {
2297
+ log.process("info", "general", `📌 Seeding with base branch build: ${c.slice(0, 7)}`);
2298
+ initialCommitsWithBuilds.push(c);
2299
+ }
2300
+ }
2301
+ }
2256
2302
  log.process("info", "general", "🔍 Resolving ancestor builds from git history...");
2257
2303
  const { parentCommits, visitedCommitsWithoutBuilds } = await getParentCommits((commits) => sendHasBuildsWithCommitsToAPI(config, apiToken, commits), {
2258
2304
  firstCommittedAtSeconds: gitInfo.firstBuildCreatedAt ?? void 0,
@@ -2372,6 +2418,90 @@ const platformRunner = async (config, apiToken) => {
2372
2418
  process.exit(1);
2373
2419
  }
2374
2420
  };
2421
+ const patchBuildRunner = async (patchBuildArg, config, apiToken) => {
2422
+ const { execSync } = await import("node:child_process");
2423
+ const parts = patchBuildArg.split("...");
2424
+ if (parts.length !== 2) {
2425
+ log.process("error", "general", `Invalid --patch-build format. Expected: head...base (e.g. feature...main)`);
2426
+ process.exit(1);
2427
+ }
2428
+ const [headRef, baseRef] = parts;
2429
+ log.process("info", "general", `🩹 Patch build: computing merge base between ${headRef} and ${baseRef}...`);
2430
+ let mergeBaseCommit;
2431
+ try {
2432
+ mergeBaseCommit = execSync(`git merge-base ${headRef} ${baseRef}`, { encoding: "utf-8" }).trim();
2433
+ } catch {
2434
+ log.process("error", "general", `Failed to compute merge base between ${headRef} and ${baseRef}`);
2435
+ process.exit(1);
2436
+ }
2437
+ log.process("info", "general", `📍 Merge base commit: ${mergeBaseCommit.slice(0, 7)}`);
2438
+ const { sendHasBuildsWithCommitsToAPI: checkCommits } = await Promise.resolve().then(() => api_exports);
2439
+ if ((await checkCommits(config, apiToken, [mergeBaseCommit])).length > 0) {
2440
+ log.process("info", "general", `✅ Build already exists for merge base ${mergeBaseCommit.slice(0, 7)}, no patch build needed.`);
2441
+ return;
2442
+ }
2443
+ const originalRef = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
2444
+ const originalBranch = execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8" }).trim();
2445
+ log.process("info", "general", `📦 Saving current state (${originalBranch} @ ${originalRef.slice(0, 7)})...`);
2446
+ let hasStash = false;
2447
+ try {
2448
+ hasStash = !execSync("git stash", { encoding: "utf-8" }).trim().includes("No local changes");
2449
+ } catch {}
2450
+ try {
2451
+ log.process("info", "general", `🔄 Checking out merge base ${mergeBaseCommit.slice(0, 7)}...`);
2452
+ execSync(`git checkout ${mergeBaseCommit}`, { stdio: "pipe" });
2453
+ log.process("info", "general", "📦 Installing dependencies...");
2454
+ try {
2455
+ execSync("pnpm install --frozen-lockfile", {
2456
+ stdio: "pipe",
2457
+ timeout: 12e4
2458
+ });
2459
+ } catch {
2460
+ try {
2461
+ execSync("npm ci", {
2462
+ stdio: "pipe",
2463
+ timeout: 12e4
2464
+ });
2465
+ } catch {
2466
+ execSync("npm install", {
2467
+ stdio: "pipe",
2468
+ timeout: 12e4
2469
+ });
2470
+ }
2471
+ }
2472
+ log.process("info", "general", "📖 Building Storybook...");
2473
+ if (config.storybookShots?.storybookUrl) try {
2474
+ execSync("npx storybook build", {
2475
+ stdio: "pipe",
2476
+ timeout: 3e5
2477
+ });
2478
+ } catch {
2479
+ log.process("info", "general", "Storybook build command failed, trying build-storybook...");
2480
+ execSync("npx build-storybook", {
2481
+ stdio: "pipe",
2482
+ timeout: 3e5
2483
+ });
2484
+ }
2485
+ const originalCommitHash = config.commitHash;
2486
+ const originalRefName = config.commitRefName;
2487
+ config.commitHash = mergeBaseCommit;
2488
+ config.commitRefName = baseRef;
2489
+ log.process("info", "general", "🚀 Running Third Eye for merge base...");
2490
+ await platformRunner(config, apiToken);
2491
+ config.commitHash = originalCommitHash;
2492
+ config.commitRefName = originalRefName;
2493
+ log.process("info", "general", `✅ Patch build complete for merge base ${mergeBaseCommit.slice(0, 7)}`);
2494
+ } finally {
2495
+ log.process("info", "general", `🔄 Restoring original state...`);
2496
+ try {
2497
+ if (originalBranch === "HEAD") execSync(`git checkout ${originalRef}`, { stdio: "pipe" });
2498
+ else execSync(`git checkout ${originalBranch}`, { stdio: "pipe" });
2499
+ if (hasStash) execSync("git stash pop", { stdio: "pipe" });
2500
+ } catch (err) {
2501
+ log.process("error", "general", `Failed to restore git state: ${err instanceof Error ? err.message : String(err)}`);
2502
+ }
2503
+ }
2504
+ };
2375
2505
  //#endregion
2376
2506
  //#region src/docker-runner/utils.ts
2377
2507
  const executeDockerRun = async ({ version }) => {
@@ -2446,7 +2576,11 @@ const generatePagesFromSitemap = async () => {
2446
2576
  //#endregion
2447
2577
  //#region src/bin.ts
2448
2578
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
2449
- const commandArgs = yargs(hideBin(process.argv)).parseSync()._;
2579
+ const args = yargs(hideBin(process.argv)).option("patch-build", {
2580
+ type: "string",
2581
+ describe: "Create a patch build for the merge base. Format: head...base (e.g. feature...main)"
2582
+ }).parseSync();
2583
+ const commandArgs = args._;
2450
2584
  const version = getVersion();
2451
2585
  if (version) log.process("info", "general", `Version: ${version}`);
2452
2586
  (async () => {
@@ -2475,6 +2609,7 @@ if (version) log.process("info", "general", `Version: ${version}`);
2475
2609
  log.process("info", "general", `🚀 Starting Lost Pixel in 'platform' mode`);
2476
2610
  const apiToken = await getPlatformApiToken(config);
2477
2611
  if (commandArgs.includes("finalize")) await sendFinalizeToAPI(config, apiToken);
2612
+ else if (args["patch-build"]) await patchBuildRunner(args["patch-build"], config, apiToken);
2478
2613
  else await platformRunner(config, apiToken);
2479
2614
  } else {
2480
2615
  log.process("info", "general", `🚀 Starting Lost Pixel in 'generateOnly' mode`);