@onlook/storybook-plugin 0.4.0-beta.17 → 0.4.0-beta.19
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 +78 -124
- package/dist/index.d.ts +1 -1
- package/dist/index.js +301 -262
- package/dist/preset/index.d.ts +1 -1
- package/dist/preset/index.js +301 -262
- package/dist/screenshot-service/index.js +78 -124
- package/dist/{storybook-onlook-plugin-BvrvMhRF.d.ts → storybook-onlook-plugin-J4qj72mN.d.ts} +7 -7
- package/package.json +2 -2
package/dist/preset/index.js
CHANGED
|
@@ -394,52 +394,6 @@ async function getBrowser() {
|
|
|
394
394
|
}
|
|
395
395
|
return browserPromise;
|
|
396
396
|
}
|
|
397
|
-
|
|
398
|
-
// src/screenshot-service/utils/capture-semaphore/capture-semaphore.ts
|
|
399
|
-
function createSemaphore(limit) {
|
|
400
|
-
const max = Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 1;
|
|
401
|
-
let active = 0;
|
|
402
|
-
const waiters = [];
|
|
403
|
-
const acquire = () => {
|
|
404
|
-
if (active < max) {
|
|
405
|
-
active += 1;
|
|
406
|
-
return Promise.resolve();
|
|
407
|
-
}
|
|
408
|
-
return new Promise((resolve) => {
|
|
409
|
-
waiters.push(() => {
|
|
410
|
-
active += 1;
|
|
411
|
-
resolve();
|
|
412
|
-
});
|
|
413
|
-
});
|
|
414
|
-
};
|
|
415
|
-
const release = () => {
|
|
416
|
-
active -= 1;
|
|
417
|
-
const next = waiters.shift();
|
|
418
|
-
if (next) next();
|
|
419
|
-
};
|
|
420
|
-
return {
|
|
421
|
-
async run(fn) {
|
|
422
|
-
await acquire();
|
|
423
|
-
try {
|
|
424
|
-
return await fn();
|
|
425
|
-
} finally {
|
|
426
|
-
release();
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
var DEFAULT_CAPTURE_CONCURRENCY = 3;
|
|
432
|
-
function resolveConcurrency(raw) {
|
|
433
|
-
if (!raw) return DEFAULT_CAPTURE_CONCURRENCY;
|
|
434
|
-
const parsed = Number.parseInt(raw, 10);
|
|
435
|
-
if (!Number.isInteger(parsed) || parsed < 1) return DEFAULT_CAPTURE_CONCURRENCY;
|
|
436
|
-
return parsed;
|
|
437
|
-
}
|
|
438
|
-
var captureSemaphore = createSemaphore(
|
|
439
|
-
resolveConcurrency(process.env.ONLOOK_CAPTURE_CONCURRENCY)
|
|
440
|
-
);
|
|
441
|
-
|
|
442
|
-
// src/screenshot-service/utils/screenshot/screenshot.ts
|
|
443
397
|
function classifyPreviewSnapshot(s) {
|
|
444
398
|
if (s.text.includes("Sorry, something went wrong")) return "errored";
|
|
445
399
|
if (s.text.includes("Couldn't find story")) return "errored";
|
|
@@ -510,89 +464,87 @@ function getScreenshotPath(storyId, theme) {
|
|
|
510
464
|
return path8.join(storyDir, `${theme}.png`);
|
|
511
465
|
}
|
|
512
466
|
async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, height = VIEWPORT_HEIGHT, storybookUrl = "http://localhost:6006", loadTimeoutMs = 9e4, renderTimeoutMs = 2e4) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
467
|
+
const browser = await getBrowser();
|
|
468
|
+
const context = await browser.newContext({
|
|
469
|
+
viewport: { width, height },
|
|
470
|
+
deviceScaleFactor: 2
|
|
471
|
+
});
|
|
472
|
+
const page = await context.newPage();
|
|
473
|
+
try {
|
|
474
|
+
const url = `${storybookUrl}/iframe.html?id=${storyId}&viewMode=story&globals=theme:${theme}`;
|
|
475
|
+
await page.goto(url, { timeout: loadTimeoutMs });
|
|
476
|
+
await page.waitForLoadState("domcontentloaded", { timeout: loadTimeoutMs });
|
|
477
|
+
await page.waitForLoadState("load", { timeout: loadTimeoutMs });
|
|
520
478
|
try {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
img.addEventListener("error", resolve);
|
|
540
|
-
setTimeout(resolve, 3e3);
|
|
541
|
-
});
|
|
542
|
-
})
|
|
543
|
-
);
|
|
544
|
-
});
|
|
545
|
-
} catch {
|
|
546
|
-
}
|
|
547
|
-
const contentBounds = await page.evaluate(() => {
|
|
548
|
-
const root = document.querySelector("#storybook-root");
|
|
549
|
-
if (!root) return null;
|
|
550
|
-
const children = root.querySelectorAll("*");
|
|
551
|
-
if (children.length === 0) return null;
|
|
552
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
553
|
-
children.forEach((child) => {
|
|
554
|
-
const rect = child.getBoundingClientRect();
|
|
555
|
-
if (rect.width === 0 || rect.height === 0) return;
|
|
556
|
-
minX = Math.min(minX, rect.left);
|
|
557
|
-
minY = Math.min(minY, rect.top);
|
|
558
|
-
maxX = Math.max(maxX, rect.right);
|
|
559
|
-
maxY = Math.max(maxY, rect.bottom);
|
|
560
|
-
});
|
|
561
|
-
if (minX === Infinity) return null;
|
|
562
|
-
return {
|
|
563
|
-
x: minX,
|
|
564
|
-
y: minY,
|
|
565
|
-
width: maxX - minX,
|
|
566
|
-
height: maxY - minY
|
|
567
|
-
};
|
|
479
|
+
await page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
480
|
+
} catch {
|
|
481
|
+
}
|
|
482
|
+
const renderState = await detectRenderState(page, { hardCapMs: renderTimeoutMs });
|
|
483
|
+
await page.evaluate(() => document.fonts.ready);
|
|
484
|
+
try {
|
|
485
|
+
await page.evaluate(async () => {
|
|
486
|
+
const images = document.querySelectorAll("img");
|
|
487
|
+
await Promise.all(
|
|
488
|
+
Array.from(images).map((img) => {
|
|
489
|
+
if (img.complete) return Promise.resolve();
|
|
490
|
+
return new Promise((resolve) => {
|
|
491
|
+
img.addEventListener("load", resolve);
|
|
492
|
+
img.addEventListener("error", resolve);
|
|
493
|
+
setTimeout(resolve, 3e3);
|
|
494
|
+
});
|
|
495
|
+
})
|
|
496
|
+
);
|
|
568
497
|
});
|
|
569
|
-
|
|
570
|
-
let resultBoundingBox = null;
|
|
571
|
-
if (contentBounds && contentBounds.width > 0 && contentBounds.height > 0) {
|
|
572
|
-
const PADDING = 20;
|
|
573
|
-
const clippedWidth = Math.min(width, contentBounds.width + PADDING);
|
|
574
|
-
const clippedHeight = Math.min(height, contentBounds.height + PADDING);
|
|
575
|
-
resultBoundingBox = {
|
|
576
|
-
width: Math.max(MIN_COMPONENT_WIDTH, Math.round(clippedWidth)),
|
|
577
|
-
height: Math.max(MIN_COMPONENT_HEIGHT, Math.round(clippedHeight))
|
|
578
|
-
};
|
|
579
|
-
screenshotBuffer = await page.screenshot({
|
|
580
|
-
type: "png",
|
|
581
|
-
clip: {
|
|
582
|
-
x: Math.max(0, contentBounds.x - 10),
|
|
583
|
-
y: Math.max(0, contentBounds.y - 10),
|
|
584
|
-
width: clippedWidth,
|
|
585
|
-
height: clippedHeight
|
|
586
|
-
}
|
|
587
|
-
});
|
|
588
|
-
} else {
|
|
589
|
-
screenshotBuffer = await page.screenshot({ type: "png" });
|
|
590
|
-
}
|
|
591
|
-
return { buffer: screenshotBuffer, boundingBox: resultBoundingBox, renderState };
|
|
592
|
-
} finally {
|
|
593
|
-
await context.close();
|
|
498
|
+
} catch {
|
|
594
499
|
}
|
|
595
|
-
|
|
500
|
+
const contentBounds = await page.evaluate(() => {
|
|
501
|
+
const root = document.querySelector("#storybook-root");
|
|
502
|
+
if (!root) return null;
|
|
503
|
+
const children = root.querySelectorAll("*");
|
|
504
|
+
if (children.length === 0) return null;
|
|
505
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
506
|
+
children.forEach((child) => {
|
|
507
|
+
const rect = child.getBoundingClientRect();
|
|
508
|
+
if (rect.width === 0 || rect.height === 0) return;
|
|
509
|
+
minX = Math.min(minX, rect.left);
|
|
510
|
+
minY = Math.min(minY, rect.top);
|
|
511
|
+
maxX = Math.max(maxX, rect.right);
|
|
512
|
+
maxY = Math.max(maxY, rect.bottom);
|
|
513
|
+
});
|
|
514
|
+
if (minX === Infinity) return null;
|
|
515
|
+
return {
|
|
516
|
+
x: minX,
|
|
517
|
+
y: minY,
|
|
518
|
+
width: maxX - minX,
|
|
519
|
+
height: maxY - minY
|
|
520
|
+
};
|
|
521
|
+
});
|
|
522
|
+
let screenshotBuffer;
|
|
523
|
+
let resultBoundingBox = null;
|
|
524
|
+
if (contentBounds && contentBounds.width > 0 && contentBounds.height > 0) {
|
|
525
|
+
const PADDING = 20;
|
|
526
|
+
const clippedWidth = Math.min(width, contentBounds.width + PADDING);
|
|
527
|
+
const clippedHeight = Math.min(height, contentBounds.height + PADDING);
|
|
528
|
+
resultBoundingBox = {
|
|
529
|
+
width: Math.max(MIN_COMPONENT_WIDTH, Math.round(clippedWidth)),
|
|
530
|
+
height: Math.max(MIN_COMPONENT_HEIGHT, Math.round(clippedHeight))
|
|
531
|
+
};
|
|
532
|
+
screenshotBuffer = await page.screenshot({
|
|
533
|
+
type: "png",
|
|
534
|
+
clip: {
|
|
535
|
+
x: Math.max(0, contentBounds.x - 10),
|
|
536
|
+
y: Math.max(0, contentBounds.y - 10),
|
|
537
|
+
width: clippedWidth,
|
|
538
|
+
height: clippedHeight
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
} else {
|
|
542
|
+
screenshotBuffer = await page.screenshot({ type: "png" });
|
|
543
|
+
}
|
|
544
|
+
return { buffer: screenshotBuffer, boundingBox: resultBoundingBox, renderState };
|
|
545
|
+
} finally {
|
|
546
|
+
await context.close();
|
|
547
|
+
}
|
|
596
548
|
}
|
|
597
549
|
async function generateScreenshot(storyId, theme, storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
598
550
|
try {
|
|
@@ -699,39 +651,7 @@ async function waitForIndexAndBroadcast(port = 6006) {
|
|
|
699
651
|
});
|
|
700
652
|
}
|
|
701
653
|
|
|
702
|
-
// src/handlers/handleStoryFileChange/captureCoalescer/captureCoalescer.ts
|
|
703
|
-
function createCaptureCoalescer(runCapture) {
|
|
704
|
-
const inFlight3 = /* @__PURE__ */ new Map();
|
|
705
|
-
const trailing2 = /* @__PURE__ */ new Set();
|
|
706
|
-
const start = (storyId) => {
|
|
707
|
-
const done = (async () => {
|
|
708
|
-
try {
|
|
709
|
-
await runCapture(storyId);
|
|
710
|
-
} finally {
|
|
711
|
-
inFlight3.delete(storyId);
|
|
712
|
-
if (trailing2.has(storyId)) {
|
|
713
|
-
trailing2.delete(storyId);
|
|
714
|
-
await start(storyId);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
})();
|
|
718
|
-
inFlight3.set(storyId, done);
|
|
719
|
-
return done;
|
|
720
|
-
};
|
|
721
|
-
return {
|
|
722
|
-
capture(storyId) {
|
|
723
|
-
const existing = inFlight3.get(storyId);
|
|
724
|
-
if (existing) {
|
|
725
|
-
trailing2.add(storyId);
|
|
726
|
-
return existing;
|
|
727
|
-
}
|
|
728
|
-
return start(storyId);
|
|
729
|
-
}
|
|
730
|
-
};
|
|
731
|
-
}
|
|
732
|
-
|
|
733
654
|
// src/handlers/handleStoryFileChange/handleStoryFileChange.ts
|
|
734
|
-
var STORYBOOK_URL = "http://localhost:6006";
|
|
735
655
|
var cachedIndex = null;
|
|
736
656
|
var indexFetchPromise = null;
|
|
737
657
|
var pendingFiles = /* @__PURE__ */ new Set();
|
|
@@ -753,43 +673,44 @@ function getStoriesForFile(filePath) {
|
|
|
753
673
|
const fileName = path8.basename(filePath);
|
|
754
674
|
return Object.values(cachedIndex.entries).filter((entry) => entry.type === "story" && entry.importPath.endsWith(fileName)).map((entry) => entry.id);
|
|
755
675
|
}
|
|
756
|
-
var latestStoryFiles = /* @__PURE__ */ new Map();
|
|
757
|
-
async function captureStoryAndUpdateManifest(storyId) {
|
|
758
|
-
const [lightResult] = await Promise.all([
|
|
759
|
-
generateScreenshot(storyId, "light", STORYBOOK_URL),
|
|
760
|
-
generateScreenshot(storyId, "dark", STORYBOOK_URL)
|
|
761
|
-
]);
|
|
762
|
-
const boundingBox = lightResult?.boundingBox ?? void 0;
|
|
763
|
-
for (const file of latestStoryFiles.get(storyId) ?? []) {
|
|
764
|
-
const fileHash = computeFileHash(file);
|
|
765
|
-
updateManifest(storyId, file, fileHash, boundingBox);
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
var captureCoalescer = createCaptureCoalescer(captureStoryAndUpdateManifest);
|
|
769
676
|
async function regenerateScreenshotsForFiles(files) {
|
|
770
677
|
await fetchStorybookIndex();
|
|
771
|
-
const
|
|
678
|
+
const allStoryIds = /* @__PURE__ */ new Set();
|
|
679
|
+
const fileToStories = /* @__PURE__ */ new Map();
|
|
772
680
|
for (const file of files) {
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
681
|
+
const storyIds = getStoriesForFile(file);
|
|
682
|
+
fileToStories.set(file, storyIds);
|
|
683
|
+
for (const id of storyIds) {
|
|
684
|
+
allStoryIds.add(id);
|
|
777
685
|
}
|
|
778
686
|
}
|
|
779
|
-
if (
|
|
687
|
+
if (allStoryIds.size === 0) {
|
|
780
688
|
console.log("[Screenshots] No stories found for changed files");
|
|
781
689
|
return;
|
|
782
690
|
}
|
|
783
691
|
console.log(
|
|
784
|
-
`[Screenshots] Regenerating ${
|
|
692
|
+
`[Screenshots] Regenerating ${allStoryIds.size} stories from ${files.length} files`
|
|
785
693
|
);
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
}
|
|
694
|
+
const storybookUrl = "http://localhost:6006";
|
|
695
|
+
const storyBoundingBoxes = /* @__PURE__ */ new Map();
|
|
789
696
|
await Promise.all(
|
|
790
|
-
Array.from(
|
|
697
|
+
Array.from(allStoryIds).map(async (storyId) => {
|
|
698
|
+
const [lightResult, _darkResult] = await Promise.all([
|
|
699
|
+
generateScreenshot(storyId, "light", storybookUrl),
|
|
700
|
+
generateScreenshot(storyId, "dark", storybookUrl)
|
|
701
|
+
]);
|
|
702
|
+
if (lightResult) {
|
|
703
|
+
storyBoundingBoxes.set(storyId, lightResult.boundingBox);
|
|
704
|
+
}
|
|
705
|
+
})
|
|
791
706
|
);
|
|
792
|
-
|
|
707
|
+
for (const [file, storyIds] of fileToStories) {
|
|
708
|
+
const fileHash = computeFileHash(file);
|
|
709
|
+
storyIds.forEach((storyId) => {
|
|
710
|
+
updateManifest(storyId, file, fileHash, storyBoundingBoxes.get(storyId));
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
console.log(`[Screenshots] \u2713 Regenerated ${allStoryIds.size} stories`);
|
|
793
714
|
}
|
|
794
715
|
function handleStoryFileChange({ file, modules }) {
|
|
795
716
|
if (file.endsWith(".stories.tsx") || file.endsWith(".stories.ts")) {
|
|
@@ -932,9 +853,139 @@ function moduleLoadCatchAllPlugin() {
|
|
|
932
853
|
};
|
|
933
854
|
}
|
|
934
855
|
|
|
935
|
-
// src/package-mock/
|
|
856
|
+
// src/package-mock/convex-mock.ts
|
|
936
857
|
var VALID_IDENT_RE2 = /^[A-Za-z_$][\w$]*$/;
|
|
937
858
|
var RESERVED_HELPER_NAMES = /* @__PURE__ */ new Set([
|
|
859
|
+
"__pass",
|
|
860
|
+
"__null",
|
|
861
|
+
"__asyncNoop",
|
|
862
|
+
"__noop",
|
|
863
|
+
"__convexClient",
|
|
864
|
+
"createElement",
|
|
865
|
+
"Fragment"
|
|
866
|
+
]);
|
|
867
|
+
var SHARED_PREAMBLE = [
|
|
868
|
+
"import { createElement, Fragment } from 'react';",
|
|
869
|
+
"const __asyncNoop = async () => {};",
|
|
870
|
+
"const __noop = () => {};",
|
|
871
|
+
// No `: any` param annotation — the virtual module is parsed as PLAIN JS.
|
|
872
|
+
"const __pass = (props) => createElement(Fragment, null, props ? props.children : undefined);",
|
|
873
|
+
"const __null = () => null;"
|
|
874
|
+
];
|
|
875
|
+
var CONVEX_CLIENT_PREAMBLE = [
|
|
876
|
+
"const __convexClient = {",
|
|
877
|
+
" setAuth: __noop,",
|
|
878
|
+
" clearAuth: __noop,",
|
|
879
|
+
" close: __asyncNoop,",
|
|
880
|
+
" connectionState: () => ({ hasInflightRequests: false, isWebSocketConnected: false }),",
|
|
881
|
+
" watchQuery: () => ({",
|
|
882
|
+
" onUpdate: () => __noop,",
|
|
883
|
+
" localQueryResult: () => undefined,",
|
|
884
|
+
" journal: () => undefined,",
|
|
885
|
+
" }),",
|
|
886
|
+
" mutation: __asyncNoop,",
|
|
887
|
+
" action: __asyncNoop,",
|
|
888
|
+
" query: __asyncNoop,",
|
|
889
|
+
"};"
|
|
890
|
+
];
|
|
891
|
+
var CONVEX_KNOWN_HOOKS = {
|
|
892
|
+
useQuery: "undefined",
|
|
893
|
+
useQueries: "({})",
|
|
894
|
+
useMutation: "__asyncNoop",
|
|
895
|
+
useAction: "__asyncNoop",
|
|
896
|
+
usePaginatedQuery: "({ results: [], status: 'LoadingFirstPage', isLoading: true, loadMore: __noop })",
|
|
897
|
+
useConvex: "__convexClient",
|
|
898
|
+
useConvexAuth: "({ isLoading: false, isAuthenticated: true })"
|
|
899
|
+
};
|
|
900
|
+
var CONVEX_CHILDREN_COMPONENTS = /* @__PURE__ */ new Set([
|
|
901
|
+
"ConvexProvider",
|
|
902
|
+
"ConvexProviderWithAuth",
|
|
903
|
+
"ConvexProviderWithClerk",
|
|
904
|
+
"Authenticated"
|
|
905
|
+
]);
|
|
906
|
+
var CONVEX_NULL_COMPONENTS = /* @__PURE__ */ new Set(["Unauthenticated", "AuthLoading"]);
|
|
907
|
+
function convexExportLine(name) {
|
|
908
|
+
if (name in CONVEX_KNOWN_HOOKS) {
|
|
909
|
+
return `export const ${name} = () => ${CONVEX_KNOWN_HOOKS[name]};`;
|
|
910
|
+
}
|
|
911
|
+
if (name === "ConvexReactClient") {
|
|
912
|
+
return "export function ConvexReactClient() { return __convexClient; }";
|
|
913
|
+
}
|
|
914
|
+
if (CONVEX_CHILDREN_COMPONENTS.has(name)) return `export const ${name} = __pass;`;
|
|
915
|
+
if (CONVEX_NULL_COMPONENTS.has(name)) return `export const ${name} = __null;`;
|
|
916
|
+
if (/^use[A-Z]/.test(name)) return `export const ${name} = () => undefined;`;
|
|
917
|
+
if (/^[A-Z]/.test(name)) return `export const ${name} = __pass;`;
|
|
918
|
+
return `export const ${name} = undefined;`;
|
|
919
|
+
}
|
|
920
|
+
function buildConvexReactMockSource(usedExports) {
|
|
921
|
+
const names = dedupeNames([
|
|
922
|
+
...usedExports,
|
|
923
|
+
...Object.keys(CONVEX_KNOWN_HOOKS),
|
|
924
|
+
...CONVEX_CHILDREN_COMPONENTS,
|
|
925
|
+
...CONVEX_NULL_COMPONENTS,
|
|
926
|
+
"ConvexReactClient"
|
|
927
|
+
]);
|
|
928
|
+
return [
|
|
929
|
+
"// Auto-generated by @onlook/storybook-plugin (package mock). Zero-backend",
|
|
930
|
+
"// Convex stand-in: queries stay in the loading state (`useQuery` returns",
|
|
931
|
+
"// `undefined`), mutations/actions no-op, auth reads signed-in. Swap back to",
|
|
932
|
+
"// the real convex package (with a deployment URL) for live data.",
|
|
933
|
+
...SHARED_PREAMBLE,
|
|
934
|
+
...CONVEX_CLIENT_PREAMBLE,
|
|
935
|
+
"",
|
|
936
|
+
...names.map(convexExportLine),
|
|
937
|
+
"",
|
|
938
|
+
"export default {};",
|
|
939
|
+
""
|
|
940
|
+
].join("\n");
|
|
941
|
+
}
|
|
942
|
+
var CONVEX_AUTH_KNOWN_HOOKS = {
|
|
943
|
+
useAuthActions: "({ signIn: __asyncNoop, signOut: __asyncNoop })",
|
|
944
|
+
useAuthToken: "null"
|
|
945
|
+
};
|
|
946
|
+
var CONVEX_AUTH_CHILDREN_COMPONENTS = /* @__PURE__ */ new Set(["ConvexAuthProvider"]);
|
|
947
|
+
function convexAuthExportLine(name) {
|
|
948
|
+
if (name in CONVEX_AUTH_KNOWN_HOOKS) {
|
|
949
|
+
return `export const ${name} = () => ${CONVEX_AUTH_KNOWN_HOOKS[name]};`;
|
|
950
|
+
}
|
|
951
|
+
if (CONVEX_AUTH_CHILDREN_COMPONENTS.has(name)) {
|
|
952
|
+
return `export const ${name} = __pass;`;
|
|
953
|
+
}
|
|
954
|
+
if (/^use[A-Z]/.test(name)) return `export const ${name} = () => undefined;`;
|
|
955
|
+
if (/^[A-Z]/.test(name)) return `export const ${name} = __pass;`;
|
|
956
|
+
return `export const ${name} = undefined;`;
|
|
957
|
+
}
|
|
958
|
+
function buildConvexAuthMockSource(usedExports) {
|
|
959
|
+
const names = dedupeNames([
|
|
960
|
+
...usedExports,
|
|
961
|
+
...Object.keys(CONVEX_AUTH_KNOWN_HOOKS),
|
|
962
|
+
...CONVEX_AUTH_CHILDREN_COMPONENTS
|
|
963
|
+
]);
|
|
964
|
+
return [
|
|
965
|
+
"// Auto-generated by @onlook/storybook-plugin (package mock). Zero-backend",
|
|
966
|
+
"// @convex-dev/auth stand-in: auth actions no-op, no token. Swap back to the",
|
|
967
|
+
"// real package for live auth flows.",
|
|
968
|
+
...SHARED_PREAMBLE,
|
|
969
|
+
"",
|
|
970
|
+
...names.map(convexAuthExportLine),
|
|
971
|
+
"",
|
|
972
|
+
"export default {};",
|
|
973
|
+
""
|
|
974
|
+
].join("\n");
|
|
975
|
+
}
|
|
976
|
+
function dedupeNames(usedExports) {
|
|
977
|
+
return Array.from(
|
|
978
|
+
new Set(
|
|
979
|
+
usedExports.filter(
|
|
980
|
+
(n) => VALID_IDENT_RE2.test(n) && n !== "default" && !RESERVED_HELPER_NAMES.has(n)
|
|
981
|
+
)
|
|
982
|
+
)
|
|
983
|
+
).sort();
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
// src/package-mock/clerk-mock.ts
|
|
987
|
+
var VALID_IDENT_RE3 = /^[A-Za-z_$][\w$]*$/;
|
|
988
|
+
var RESERVED_HELPER_NAMES2 = /* @__PURE__ */ new Set([
|
|
938
989
|
"__user",
|
|
939
990
|
"__pass",
|
|
940
991
|
"__null",
|
|
@@ -947,7 +998,9 @@ var RESERVED_HELPER_NAMES = /* @__PURE__ */ new Set([
|
|
|
947
998
|
]);
|
|
948
999
|
var BUILDERS = {
|
|
949
1000
|
"@clerk/nextjs": buildClerkMockSource,
|
|
950
|
-
"@clerk/clerk-react": buildClerkMockSource
|
|
1001
|
+
"@clerk/clerk-react": buildClerkMockSource,
|
|
1002
|
+
"convex/react": buildConvexReactMockSource,
|
|
1003
|
+
"@convex-dev/auth/react": buildConvexAuthMockSource
|
|
951
1004
|
};
|
|
952
1005
|
function isKnownMockPackage(pkg) {
|
|
953
1006
|
return pkg in BUILDERS;
|
|
@@ -1071,7 +1124,7 @@ function buildClerkMockSource(usedExports) {
|
|
|
1071
1124
|
const names = Array.from(
|
|
1072
1125
|
new Set(
|
|
1073
1126
|
usedExports.filter(
|
|
1074
|
-
(n) =>
|
|
1127
|
+
(n) => VALID_IDENT_RE3.test(n) && n !== "default" && !RESERVED_HELPER_NAMES2.has(n)
|
|
1075
1128
|
)
|
|
1076
1129
|
)
|
|
1077
1130
|
).sort();
|
|
@@ -1127,63 +1180,61 @@ function packageMockPlugin(options) {
|
|
|
1127
1180
|
|
|
1128
1181
|
// src/screenshot-service/utils/console-logs/console-logs.ts
|
|
1129
1182
|
async function captureConsoleLogs(storyId, theme = "light", storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
timestamp: Date.now()
|
|
1145
|
-
});
|
|
1146
|
-
});
|
|
1147
|
-
page.on("pageerror", (err) => {
|
|
1148
|
-
pageErrors.push(err.message);
|
|
1149
|
-
});
|
|
1150
|
-
page.on("response", (res) => {
|
|
1151
|
-
const status = res.status();
|
|
1152
|
-
if (status >= 400) {
|
|
1153
|
-
const req = res.request();
|
|
1154
|
-
failedRequests.push({
|
|
1155
|
-
url: res.url(),
|
|
1156
|
-
status,
|
|
1157
|
-
method: req.method(),
|
|
1158
|
-
resourceType: req.resourceType(),
|
|
1159
|
-
failure: null
|
|
1160
|
-
});
|
|
1161
|
-
}
|
|
1183
|
+
const browser = await getBrowser();
|
|
1184
|
+
const context = await browser.newContext({
|
|
1185
|
+
viewport: { width: VIEWPORT_WIDTH, height: VIEWPORT_HEIGHT }
|
|
1186
|
+
});
|
|
1187
|
+
const page = await context.newPage();
|
|
1188
|
+
const logs = [];
|
|
1189
|
+
const pageErrors = [];
|
|
1190
|
+
const failedRequests = [];
|
|
1191
|
+
page.on("console", (msg) => {
|
|
1192
|
+
const level = msg.type();
|
|
1193
|
+
logs.push({
|
|
1194
|
+
level: level === "warning" ? "warn" : level,
|
|
1195
|
+
text: msg.text(),
|
|
1196
|
+
timestamp: Date.now()
|
|
1162
1197
|
});
|
|
1163
|
-
|
|
1198
|
+
});
|
|
1199
|
+
page.on("pageerror", (err) => {
|
|
1200
|
+
pageErrors.push(err.message);
|
|
1201
|
+
});
|
|
1202
|
+
page.on("response", (res) => {
|
|
1203
|
+
const status = res.status();
|
|
1204
|
+
if (status >= 400) {
|
|
1205
|
+
const req = res.request();
|
|
1164
1206
|
failedRequests.push({
|
|
1165
|
-
url:
|
|
1166
|
-
status
|
|
1207
|
+
url: res.url(),
|
|
1208
|
+
status,
|
|
1167
1209
|
method: req.method(),
|
|
1168
1210
|
resourceType: req.resourceType(),
|
|
1169
|
-
failure:
|
|
1211
|
+
failure: null
|
|
1170
1212
|
});
|
|
1213
|
+
}
|
|
1214
|
+
});
|
|
1215
|
+
page.on("requestfailed", (req) => {
|
|
1216
|
+
failedRequests.push({
|
|
1217
|
+
url: req.url(),
|
|
1218
|
+
status: null,
|
|
1219
|
+
method: req.method(),
|
|
1220
|
+
resourceType: req.resourceType(),
|
|
1221
|
+
failure: req.failure()?.errorText ?? "request failed"
|
|
1171
1222
|
});
|
|
1172
|
-
|
|
1223
|
+
});
|
|
1224
|
+
const url = `${storybookUrl}/iframe.html?id=${storyId}&viewMode=story&globals=theme:${theme}`;
|
|
1225
|
+
try {
|
|
1226
|
+
await page.goto(url, { timeout: timeoutMs });
|
|
1227
|
+
await page.waitForLoadState("domcontentloaded", { timeout: timeoutMs });
|
|
1228
|
+
await page.waitForLoadState("load", { timeout: timeoutMs });
|
|
1173
1229
|
try {
|
|
1174
|
-
await page.
|
|
1175
|
-
|
|
1176
|
-
await page.waitForLoadState("load", { timeout: timeoutMs });
|
|
1177
|
-
try {
|
|
1178
|
-
await page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
1179
|
-
} catch {
|
|
1180
|
-
}
|
|
1181
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
1182
|
-
return { logs, pageErrors, failedRequests, url };
|
|
1183
|
-
} finally {
|
|
1184
|
-
await context.close();
|
|
1230
|
+
await page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
1231
|
+
} catch {
|
|
1185
1232
|
}
|
|
1186
|
-
|
|
1233
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
1234
|
+
return { logs, pageErrors, failedRequests, url };
|
|
1235
|
+
} finally {
|
|
1236
|
+
await context.close();
|
|
1237
|
+
}
|
|
1187
1238
|
}
|
|
1188
1239
|
|
|
1189
1240
|
// src/soft-story-rerender/soft-story-rerender.ts
|
|
@@ -1374,17 +1425,6 @@ function findGitRoot(startPath) {
|
|
|
1374
1425
|
}
|
|
1375
1426
|
return null;
|
|
1376
1427
|
}
|
|
1377
|
-
function ifNoneMatchHits(header, etag) {
|
|
1378
|
-
if (!header) return false;
|
|
1379
|
-
return header.split(",").some((raw) => {
|
|
1380
|
-
const token = raw.trim();
|
|
1381
|
-
return token === etag || token === `W/${etag}`;
|
|
1382
|
-
});
|
|
1383
|
-
}
|
|
1384
|
-
function decideIndexResponse(body, ifNoneMatch) {
|
|
1385
|
-
const etag = `"${createHash("sha1").update(body).digest("hex")}"`;
|
|
1386
|
-
return { etag, notModified: ifNoneMatchHits(ifNoneMatch, etag) };
|
|
1387
|
-
}
|
|
1388
1428
|
var STORY_FILE_PATTERN = /\.stories\.(js|jsx|mjs|ts|tsx|mdx)$/;
|
|
1389
1429
|
function isStoryFile(file) {
|
|
1390
1430
|
return STORY_FILE_PATTERN.test(file);
|
|
@@ -1417,7 +1457,8 @@ function portFromArgv(argv) {
|
|
|
1417
1457
|
}
|
|
1418
1458
|
async function defaultFetchIndex(port) {
|
|
1419
1459
|
try {
|
|
1420
|
-
const res = await fetch(`http://localhost:${port}/index.json`, {
|
|
1460
|
+
const res = await fetch(`http://localhost:${port}/index.json?_t=${Date.now()}`, {
|
|
1461
|
+
cache: "no-store",
|
|
1421
1462
|
signal: AbortSignal.timeout(2e3)
|
|
1422
1463
|
});
|
|
1423
1464
|
if (!res.ok) return null;
|
|
@@ -1577,8 +1618,15 @@ function detectTailwindEntryCss(cwd) {
|
|
|
1577
1618
|
var serveMetadataAndScreenshots = (req, res, next) => {
|
|
1578
1619
|
if (req.url === "/onbook-index.json") {
|
|
1579
1620
|
console.log("[STORYBOOK_PLUGIN] Serving /onbook-index.json endpoint");
|
|
1621
|
+
const cacheBuster = Date.now();
|
|
1580
1622
|
console.log("[STORYBOOK_PLUGIN] Fetching http://localhost:6006/index.json");
|
|
1581
|
-
fetch(
|
|
1623
|
+
fetch(`http://localhost:6006/index.json?_t=${cacheBuster}`, {
|
|
1624
|
+
cache: "no-store",
|
|
1625
|
+
headers: {
|
|
1626
|
+
"Cache-Control": "no-cache",
|
|
1627
|
+
Pragma: "no-cache"
|
|
1628
|
+
}
|
|
1629
|
+
}).then((response) => {
|
|
1582
1630
|
console.log("[STORYBOOK_PLUGIN] Storybook index fetch response", {
|
|
1583
1631
|
status: response.status,
|
|
1584
1632
|
ok: response.ok,
|
|
@@ -1597,21 +1645,12 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1597
1645
|
entryCount: Object.keys(indexData.entries || {}).length,
|
|
1598
1646
|
hasMetadata: !!indexData.meta
|
|
1599
1647
|
});
|
|
1600
|
-
const body = JSON.stringify(indexData);
|
|
1601
|
-
const { etag, notModified } = decideIndexResponse(
|
|
1602
|
-
body,
|
|
1603
|
-
req.headers["if-none-match"]
|
|
1604
|
-
);
|
|
1605
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1606
|
-
res.setHeader("ETag", etag);
|
|
1607
|
-
res.setHeader("Cache-Control", "no-cache");
|
|
1608
|
-
if (notModified) {
|
|
1609
|
-
res.statusCode = 304;
|
|
1610
|
-
res.end();
|
|
1611
|
-
return;
|
|
1612
|
-
}
|
|
1613
1648
|
res.setHeader("Content-Type", "application/json");
|
|
1614
|
-
res.
|
|
1649
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1650
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
1651
|
+
res.setHeader("Pragma", "no-cache");
|
|
1652
|
+
res.setHeader("Expires", "0");
|
|
1653
|
+
res.end(JSON.stringify(indexData));
|
|
1615
1654
|
}).catch((error) => {
|
|
1616
1655
|
console.error("[STORYBOOK_PLUGIN] Failed to fetch/extend index.json", {
|
|
1617
1656
|
error: error instanceof Error ? error.message : String(error),
|