@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/index.js
CHANGED
|
@@ -514,52 +514,6 @@ async function getBrowser() {
|
|
|
514
514
|
}
|
|
515
515
|
return browserPromise;
|
|
516
516
|
}
|
|
517
|
-
|
|
518
|
-
// src/screenshot-service/utils/capture-semaphore/capture-semaphore.ts
|
|
519
|
-
function createSemaphore(limit) {
|
|
520
|
-
const max = Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 1;
|
|
521
|
-
let active = 0;
|
|
522
|
-
const waiters = [];
|
|
523
|
-
const acquire = () => {
|
|
524
|
-
if (active < max) {
|
|
525
|
-
active += 1;
|
|
526
|
-
return Promise.resolve();
|
|
527
|
-
}
|
|
528
|
-
return new Promise((resolve) => {
|
|
529
|
-
waiters.push(() => {
|
|
530
|
-
active += 1;
|
|
531
|
-
resolve();
|
|
532
|
-
});
|
|
533
|
-
});
|
|
534
|
-
};
|
|
535
|
-
const release = () => {
|
|
536
|
-
active -= 1;
|
|
537
|
-
const next = waiters.shift();
|
|
538
|
-
if (next) next();
|
|
539
|
-
};
|
|
540
|
-
return {
|
|
541
|
-
async run(fn) {
|
|
542
|
-
await acquire();
|
|
543
|
-
try {
|
|
544
|
-
return await fn();
|
|
545
|
-
} finally {
|
|
546
|
-
release();
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
var DEFAULT_CAPTURE_CONCURRENCY = 3;
|
|
552
|
-
function resolveConcurrency(raw) {
|
|
553
|
-
if (!raw) return DEFAULT_CAPTURE_CONCURRENCY;
|
|
554
|
-
const parsed = Number.parseInt(raw, 10);
|
|
555
|
-
if (!Number.isInteger(parsed) || parsed < 1) return DEFAULT_CAPTURE_CONCURRENCY;
|
|
556
|
-
return parsed;
|
|
557
|
-
}
|
|
558
|
-
var captureSemaphore = createSemaphore(
|
|
559
|
-
resolveConcurrency(process.env.ONLOOK_CAPTURE_CONCURRENCY)
|
|
560
|
-
);
|
|
561
|
-
|
|
562
|
-
// src/screenshot-service/utils/screenshot/screenshot.ts
|
|
563
517
|
function classifyPreviewSnapshot(s) {
|
|
564
518
|
if (s.text.includes("Sorry, something went wrong")) return "errored";
|
|
565
519
|
if (s.text.includes("Couldn't find story")) return "errored";
|
|
@@ -630,89 +584,87 @@ function getScreenshotPath(storyId, theme) {
|
|
|
630
584
|
return path8.join(storyDir, `${theme}.png`);
|
|
631
585
|
}
|
|
632
586
|
async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, height = VIEWPORT_HEIGHT, storybookUrl = "http://localhost:6006", loadTimeoutMs = 9e4, renderTimeoutMs = 2e4) {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
587
|
+
const browser = await getBrowser();
|
|
588
|
+
const context = await browser.newContext({
|
|
589
|
+
viewport: { width, height },
|
|
590
|
+
deviceScaleFactor: 2
|
|
591
|
+
});
|
|
592
|
+
const page = await context.newPage();
|
|
593
|
+
try {
|
|
594
|
+
const url = `${storybookUrl}/iframe.html?id=${storyId}&viewMode=story&globals=theme:${theme}`;
|
|
595
|
+
await page.goto(url, { timeout: loadTimeoutMs });
|
|
596
|
+
await page.waitForLoadState("domcontentloaded", { timeout: loadTimeoutMs });
|
|
597
|
+
await page.waitForLoadState("load", { timeout: loadTimeoutMs });
|
|
640
598
|
try {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
img.addEventListener("error", resolve);
|
|
660
|
-
setTimeout(resolve, 3e3);
|
|
661
|
-
});
|
|
662
|
-
})
|
|
663
|
-
);
|
|
664
|
-
});
|
|
665
|
-
} catch {
|
|
666
|
-
}
|
|
667
|
-
const contentBounds = await page.evaluate(() => {
|
|
668
|
-
const root = document.querySelector("#storybook-root");
|
|
669
|
-
if (!root) return null;
|
|
670
|
-
const children = root.querySelectorAll("*");
|
|
671
|
-
if (children.length === 0) return null;
|
|
672
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
673
|
-
children.forEach((child) => {
|
|
674
|
-
const rect = child.getBoundingClientRect();
|
|
675
|
-
if (rect.width === 0 || rect.height === 0) return;
|
|
676
|
-
minX = Math.min(minX, rect.left);
|
|
677
|
-
minY = Math.min(minY, rect.top);
|
|
678
|
-
maxX = Math.max(maxX, rect.right);
|
|
679
|
-
maxY = Math.max(maxY, rect.bottom);
|
|
680
|
-
});
|
|
681
|
-
if (minX === Infinity) return null;
|
|
682
|
-
return {
|
|
683
|
-
x: minX,
|
|
684
|
-
y: minY,
|
|
685
|
-
width: maxX - minX,
|
|
686
|
-
height: maxY - minY
|
|
687
|
-
};
|
|
599
|
+
await page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
600
|
+
} catch {
|
|
601
|
+
}
|
|
602
|
+
const renderState = await detectRenderState(page, { hardCapMs: renderTimeoutMs });
|
|
603
|
+
await page.evaluate(() => document.fonts.ready);
|
|
604
|
+
try {
|
|
605
|
+
await page.evaluate(async () => {
|
|
606
|
+
const images = document.querySelectorAll("img");
|
|
607
|
+
await Promise.all(
|
|
608
|
+
Array.from(images).map((img) => {
|
|
609
|
+
if (img.complete) return Promise.resolve();
|
|
610
|
+
return new Promise((resolve) => {
|
|
611
|
+
img.addEventListener("load", resolve);
|
|
612
|
+
img.addEventListener("error", resolve);
|
|
613
|
+
setTimeout(resolve, 3e3);
|
|
614
|
+
});
|
|
615
|
+
})
|
|
616
|
+
);
|
|
688
617
|
});
|
|
689
|
-
|
|
690
|
-
let resultBoundingBox = null;
|
|
691
|
-
if (contentBounds && contentBounds.width > 0 && contentBounds.height > 0) {
|
|
692
|
-
const PADDING = 20;
|
|
693
|
-
const clippedWidth = Math.min(width, contentBounds.width + PADDING);
|
|
694
|
-
const clippedHeight = Math.min(height, contentBounds.height + PADDING);
|
|
695
|
-
resultBoundingBox = {
|
|
696
|
-
width: Math.max(MIN_COMPONENT_WIDTH, Math.round(clippedWidth)),
|
|
697
|
-
height: Math.max(MIN_COMPONENT_HEIGHT, Math.round(clippedHeight))
|
|
698
|
-
};
|
|
699
|
-
screenshotBuffer = await page.screenshot({
|
|
700
|
-
type: "png",
|
|
701
|
-
clip: {
|
|
702
|
-
x: Math.max(0, contentBounds.x - 10),
|
|
703
|
-
y: Math.max(0, contentBounds.y - 10),
|
|
704
|
-
width: clippedWidth,
|
|
705
|
-
height: clippedHeight
|
|
706
|
-
}
|
|
707
|
-
});
|
|
708
|
-
} else {
|
|
709
|
-
screenshotBuffer = await page.screenshot({ type: "png" });
|
|
710
|
-
}
|
|
711
|
-
return { buffer: screenshotBuffer, boundingBox: resultBoundingBox, renderState };
|
|
712
|
-
} finally {
|
|
713
|
-
await context.close();
|
|
618
|
+
} catch {
|
|
714
619
|
}
|
|
715
|
-
|
|
620
|
+
const contentBounds = await page.evaluate(() => {
|
|
621
|
+
const root = document.querySelector("#storybook-root");
|
|
622
|
+
if (!root) return null;
|
|
623
|
+
const children = root.querySelectorAll("*");
|
|
624
|
+
if (children.length === 0) return null;
|
|
625
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
626
|
+
children.forEach((child) => {
|
|
627
|
+
const rect = child.getBoundingClientRect();
|
|
628
|
+
if (rect.width === 0 || rect.height === 0) return;
|
|
629
|
+
minX = Math.min(minX, rect.left);
|
|
630
|
+
minY = Math.min(minY, rect.top);
|
|
631
|
+
maxX = Math.max(maxX, rect.right);
|
|
632
|
+
maxY = Math.max(maxY, rect.bottom);
|
|
633
|
+
});
|
|
634
|
+
if (minX === Infinity) return null;
|
|
635
|
+
return {
|
|
636
|
+
x: minX,
|
|
637
|
+
y: minY,
|
|
638
|
+
width: maxX - minX,
|
|
639
|
+
height: maxY - minY
|
|
640
|
+
};
|
|
641
|
+
});
|
|
642
|
+
let screenshotBuffer;
|
|
643
|
+
let resultBoundingBox = null;
|
|
644
|
+
if (contentBounds && contentBounds.width > 0 && contentBounds.height > 0) {
|
|
645
|
+
const PADDING = 20;
|
|
646
|
+
const clippedWidth = Math.min(width, contentBounds.width + PADDING);
|
|
647
|
+
const clippedHeight = Math.min(height, contentBounds.height + PADDING);
|
|
648
|
+
resultBoundingBox = {
|
|
649
|
+
width: Math.max(MIN_COMPONENT_WIDTH, Math.round(clippedWidth)),
|
|
650
|
+
height: Math.max(MIN_COMPONENT_HEIGHT, Math.round(clippedHeight))
|
|
651
|
+
};
|
|
652
|
+
screenshotBuffer = await page.screenshot({
|
|
653
|
+
type: "png",
|
|
654
|
+
clip: {
|
|
655
|
+
x: Math.max(0, contentBounds.x - 10),
|
|
656
|
+
y: Math.max(0, contentBounds.y - 10),
|
|
657
|
+
width: clippedWidth,
|
|
658
|
+
height: clippedHeight
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
} else {
|
|
662
|
+
screenshotBuffer = await page.screenshot({ type: "png" });
|
|
663
|
+
}
|
|
664
|
+
return { buffer: screenshotBuffer, boundingBox: resultBoundingBox, renderState };
|
|
665
|
+
} finally {
|
|
666
|
+
await context.close();
|
|
667
|
+
}
|
|
716
668
|
}
|
|
717
669
|
async function generateScreenshot(storyId, theme, storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
718
670
|
try {
|
|
@@ -819,39 +771,7 @@ async function waitForIndexAndBroadcast(port = 6006) {
|
|
|
819
771
|
});
|
|
820
772
|
}
|
|
821
773
|
|
|
822
|
-
// src/handlers/handleStoryFileChange/captureCoalescer/captureCoalescer.ts
|
|
823
|
-
function createCaptureCoalescer(runCapture) {
|
|
824
|
-
const inFlight2 = /* @__PURE__ */ new Map();
|
|
825
|
-
const trailing = /* @__PURE__ */ new Set();
|
|
826
|
-
const start = (storyId) => {
|
|
827
|
-
const done = (async () => {
|
|
828
|
-
try {
|
|
829
|
-
await runCapture(storyId);
|
|
830
|
-
} finally {
|
|
831
|
-
inFlight2.delete(storyId);
|
|
832
|
-
if (trailing.has(storyId)) {
|
|
833
|
-
trailing.delete(storyId);
|
|
834
|
-
await start(storyId);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
})();
|
|
838
|
-
inFlight2.set(storyId, done);
|
|
839
|
-
return done;
|
|
840
|
-
};
|
|
841
|
-
return {
|
|
842
|
-
capture(storyId) {
|
|
843
|
-
const existing = inFlight2.get(storyId);
|
|
844
|
-
if (existing) {
|
|
845
|
-
trailing.add(storyId);
|
|
846
|
-
return existing;
|
|
847
|
-
}
|
|
848
|
-
return start(storyId);
|
|
849
|
-
}
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
|
-
|
|
853
774
|
// src/handlers/handleStoryFileChange/handleStoryFileChange.ts
|
|
854
|
-
var STORYBOOK_URL = "http://localhost:6006";
|
|
855
775
|
var cachedIndex = null;
|
|
856
776
|
var indexFetchPromise = null;
|
|
857
777
|
var pendingFiles = /* @__PURE__ */ new Set();
|
|
@@ -873,43 +793,44 @@ function getStoriesForFile(filePath) {
|
|
|
873
793
|
const fileName = path8.basename(filePath);
|
|
874
794
|
return Object.values(cachedIndex.entries).filter((entry) => entry.type === "story" && entry.importPath.endsWith(fileName)).map((entry) => entry.id);
|
|
875
795
|
}
|
|
876
|
-
var latestStoryFiles = /* @__PURE__ */ new Map();
|
|
877
|
-
async function captureStoryAndUpdateManifest(storyId) {
|
|
878
|
-
const [lightResult] = await Promise.all([
|
|
879
|
-
generateScreenshot(storyId, "light", STORYBOOK_URL),
|
|
880
|
-
generateScreenshot(storyId, "dark", STORYBOOK_URL)
|
|
881
|
-
]);
|
|
882
|
-
const boundingBox = lightResult?.boundingBox ?? void 0;
|
|
883
|
-
for (const file of latestStoryFiles.get(storyId) ?? []) {
|
|
884
|
-
const fileHash = computeFileHash(file);
|
|
885
|
-
updateManifest(storyId, file, fileHash, boundingBox);
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
var captureCoalescer = createCaptureCoalescer(captureStoryAndUpdateManifest);
|
|
889
796
|
async function regenerateScreenshotsForFiles(files) {
|
|
890
797
|
await fetchStorybookIndex();
|
|
891
|
-
const
|
|
798
|
+
const allStoryIds = /* @__PURE__ */ new Set();
|
|
799
|
+
const fileToStories = /* @__PURE__ */ new Map();
|
|
892
800
|
for (const file of files) {
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
801
|
+
const storyIds = getStoriesForFile(file);
|
|
802
|
+
fileToStories.set(file, storyIds);
|
|
803
|
+
for (const id of storyIds) {
|
|
804
|
+
allStoryIds.add(id);
|
|
897
805
|
}
|
|
898
806
|
}
|
|
899
|
-
if (
|
|
807
|
+
if (allStoryIds.size === 0) {
|
|
900
808
|
console.log("[Screenshots] No stories found for changed files");
|
|
901
809
|
return;
|
|
902
810
|
}
|
|
903
811
|
console.log(
|
|
904
|
-
`[Screenshots] Regenerating ${
|
|
812
|
+
`[Screenshots] Regenerating ${allStoryIds.size} stories from ${files.length} files`
|
|
905
813
|
);
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
}
|
|
814
|
+
const storybookUrl = "http://localhost:6006";
|
|
815
|
+
const storyBoundingBoxes = /* @__PURE__ */ new Map();
|
|
909
816
|
await Promise.all(
|
|
910
|
-
Array.from(
|
|
817
|
+
Array.from(allStoryIds).map(async (storyId) => {
|
|
818
|
+
const [lightResult, _darkResult] = await Promise.all([
|
|
819
|
+
generateScreenshot(storyId, "light", storybookUrl),
|
|
820
|
+
generateScreenshot(storyId, "dark", storybookUrl)
|
|
821
|
+
]);
|
|
822
|
+
if (lightResult) {
|
|
823
|
+
storyBoundingBoxes.set(storyId, lightResult.boundingBox);
|
|
824
|
+
}
|
|
825
|
+
})
|
|
911
826
|
);
|
|
912
|
-
|
|
827
|
+
for (const [file, storyIds] of fileToStories) {
|
|
828
|
+
const fileHash = computeFileHash(file);
|
|
829
|
+
storyIds.forEach((storyId) => {
|
|
830
|
+
updateManifest(storyId, file, fileHash, storyBoundingBoxes.get(storyId));
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
console.log(`[Screenshots] \u2713 Regenerated ${allStoryIds.size} stories`);
|
|
913
834
|
}
|
|
914
835
|
function handleStoryFileChange({ file, modules }) {
|
|
915
836
|
if (file.endsWith(".stories.tsx") || file.endsWith(".stories.ts")) {
|
|
@@ -936,9 +857,139 @@ function handleStoryFileChange({ file, modules }) {
|
|
|
936
857
|
}
|
|
937
858
|
}
|
|
938
859
|
|
|
939
|
-
// src/package-mock/
|
|
860
|
+
// src/package-mock/convex-mock.ts
|
|
940
861
|
var VALID_IDENT_RE2 = /^[A-Za-z_$][\w$]*$/;
|
|
941
862
|
var RESERVED_HELPER_NAMES = /* @__PURE__ */ new Set([
|
|
863
|
+
"__pass",
|
|
864
|
+
"__null",
|
|
865
|
+
"__asyncNoop",
|
|
866
|
+
"__noop",
|
|
867
|
+
"__convexClient",
|
|
868
|
+
"createElement",
|
|
869
|
+
"Fragment"
|
|
870
|
+
]);
|
|
871
|
+
var SHARED_PREAMBLE = [
|
|
872
|
+
"import { createElement, Fragment } from 'react';",
|
|
873
|
+
"const __asyncNoop = async () => {};",
|
|
874
|
+
"const __noop = () => {};",
|
|
875
|
+
// No `: any` param annotation — the virtual module is parsed as PLAIN JS.
|
|
876
|
+
"const __pass = (props) => createElement(Fragment, null, props ? props.children : undefined);",
|
|
877
|
+
"const __null = () => null;"
|
|
878
|
+
];
|
|
879
|
+
var CONVEX_CLIENT_PREAMBLE = [
|
|
880
|
+
"const __convexClient = {",
|
|
881
|
+
" setAuth: __noop,",
|
|
882
|
+
" clearAuth: __noop,",
|
|
883
|
+
" close: __asyncNoop,",
|
|
884
|
+
" connectionState: () => ({ hasInflightRequests: false, isWebSocketConnected: false }),",
|
|
885
|
+
" watchQuery: () => ({",
|
|
886
|
+
" onUpdate: () => __noop,",
|
|
887
|
+
" localQueryResult: () => undefined,",
|
|
888
|
+
" journal: () => undefined,",
|
|
889
|
+
" }),",
|
|
890
|
+
" mutation: __asyncNoop,",
|
|
891
|
+
" action: __asyncNoop,",
|
|
892
|
+
" query: __asyncNoop,",
|
|
893
|
+
"};"
|
|
894
|
+
];
|
|
895
|
+
var CONVEX_KNOWN_HOOKS = {
|
|
896
|
+
useQuery: "undefined",
|
|
897
|
+
useQueries: "({})",
|
|
898
|
+
useMutation: "__asyncNoop",
|
|
899
|
+
useAction: "__asyncNoop",
|
|
900
|
+
usePaginatedQuery: "({ results: [], status: 'LoadingFirstPage', isLoading: true, loadMore: __noop })",
|
|
901
|
+
useConvex: "__convexClient",
|
|
902
|
+
useConvexAuth: "({ isLoading: false, isAuthenticated: true })"
|
|
903
|
+
};
|
|
904
|
+
var CONVEX_CHILDREN_COMPONENTS = /* @__PURE__ */ new Set([
|
|
905
|
+
"ConvexProvider",
|
|
906
|
+
"ConvexProviderWithAuth",
|
|
907
|
+
"ConvexProviderWithClerk",
|
|
908
|
+
"Authenticated"
|
|
909
|
+
]);
|
|
910
|
+
var CONVEX_NULL_COMPONENTS = /* @__PURE__ */ new Set(["Unauthenticated", "AuthLoading"]);
|
|
911
|
+
function convexExportLine(name) {
|
|
912
|
+
if (name in CONVEX_KNOWN_HOOKS) {
|
|
913
|
+
return `export const ${name} = () => ${CONVEX_KNOWN_HOOKS[name]};`;
|
|
914
|
+
}
|
|
915
|
+
if (name === "ConvexReactClient") {
|
|
916
|
+
return "export function ConvexReactClient() { return __convexClient; }";
|
|
917
|
+
}
|
|
918
|
+
if (CONVEX_CHILDREN_COMPONENTS.has(name)) return `export const ${name} = __pass;`;
|
|
919
|
+
if (CONVEX_NULL_COMPONENTS.has(name)) return `export const ${name} = __null;`;
|
|
920
|
+
if (/^use[A-Z]/.test(name)) return `export const ${name} = () => undefined;`;
|
|
921
|
+
if (/^[A-Z]/.test(name)) return `export const ${name} = __pass;`;
|
|
922
|
+
return `export const ${name} = undefined;`;
|
|
923
|
+
}
|
|
924
|
+
function buildConvexReactMockSource(usedExports) {
|
|
925
|
+
const names = dedupeNames([
|
|
926
|
+
...usedExports,
|
|
927
|
+
...Object.keys(CONVEX_KNOWN_HOOKS),
|
|
928
|
+
...CONVEX_CHILDREN_COMPONENTS,
|
|
929
|
+
...CONVEX_NULL_COMPONENTS,
|
|
930
|
+
"ConvexReactClient"
|
|
931
|
+
]);
|
|
932
|
+
return [
|
|
933
|
+
"// Auto-generated by @onlook/storybook-plugin (package mock). Zero-backend",
|
|
934
|
+
"// Convex stand-in: queries stay in the loading state (`useQuery` returns",
|
|
935
|
+
"// `undefined`), mutations/actions no-op, auth reads signed-in. Swap back to",
|
|
936
|
+
"// the real convex package (with a deployment URL) for live data.",
|
|
937
|
+
...SHARED_PREAMBLE,
|
|
938
|
+
...CONVEX_CLIENT_PREAMBLE,
|
|
939
|
+
"",
|
|
940
|
+
...names.map(convexExportLine),
|
|
941
|
+
"",
|
|
942
|
+
"export default {};",
|
|
943
|
+
""
|
|
944
|
+
].join("\n");
|
|
945
|
+
}
|
|
946
|
+
var CONVEX_AUTH_KNOWN_HOOKS = {
|
|
947
|
+
useAuthActions: "({ signIn: __asyncNoop, signOut: __asyncNoop })",
|
|
948
|
+
useAuthToken: "null"
|
|
949
|
+
};
|
|
950
|
+
var CONVEX_AUTH_CHILDREN_COMPONENTS = /* @__PURE__ */ new Set(["ConvexAuthProvider"]);
|
|
951
|
+
function convexAuthExportLine(name) {
|
|
952
|
+
if (name in CONVEX_AUTH_KNOWN_HOOKS) {
|
|
953
|
+
return `export const ${name} = () => ${CONVEX_AUTH_KNOWN_HOOKS[name]};`;
|
|
954
|
+
}
|
|
955
|
+
if (CONVEX_AUTH_CHILDREN_COMPONENTS.has(name)) {
|
|
956
|
+
return `export const ${name} = __pass;`;
|
|
957
|
+
}
|
|
958
|
+
if (/^use[A-Z]/.test(name)) return `export const ${name} = () => undefined;`;
|
|
959
|
+
if (/^[A-Z]/.test(name)) return `export const ${name} = __pass;`;
|
|
960
|
+
return `export const ${name} = undefined;`;
|
|
961
|
+
}
|
|
962
|
+
function buildConvexAuthMockSource(usedExports) {
|
|
963
|
+
const names = dedupeNames([
|
|
964
|
+
...usedExports,
|
|
965
|
+
...Object.keys(CONVEX_AUTH_KNOWN_HOOKS),
|
|
966
|
+
...CONVEX_AUTH_CHILDREN_COMPONENTS
|
|
967
|
+
]);
|
|
968
|
+
return [
|
|
969
|
+
"// Auto-generated by @onlook/storybook-plugin (package mock). Zero-backend",
|
|
970
|
+
"// @convex-dev/auth stand-in: auth actions no-op, no token. Swap back to the",
|
|
971
|
+
"// real package for live auth flows.",
|
|
972
|
+
...SHARED_PREAMBLE,
|
|
973
|
+
"",
|
|
974
|
+
...names.map(convexAuthExportLine),
|
|
975
|
+
"",
|
|
976
|
+
"export default {};",
|
|
977
|
+
""
|
|
978
|
+
].join("\n");
|
|
979
|
+
}
|
|
980
|
+
function dedupeNames(usedExports) {
|
|
981
|
+
return Array.from(
|
|
982
|
+
new Set(
|
|
983
|
+
usedExports.filter(
|
|
984
|
+
(n) => VALID_IDENT_RE2.test(n) && n !== "default" && !RESERVED_HELPER_NAMES.has(n)
|
|
985
|
+
)
|
|
986
|
+
)
|
|
987
|
+
).sort();
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// src/package-mock/clerk-mock.ts
|
|
991
|
+
var VALID_IDENT_RE3 = /^[A-Za-z_$][\w$]*$/;
|
|
992
|
+
var RESERVED_HELPER_NAMES2 = /* @__PURE__ */ new Set([
|
|
942
993
|
"__user",
|
|
943
994
|
"__pass",
|
|
944
995
|
"__null",
|
|
@@ -951,7 +1002,9 @@ var RESERVED_HELPER_NAMES = /* @__PURE__ */ new Set([
|
|
|
951
1002
|
]);
|
|
952
1003
|
var BUILDERS = {
|
|
953
1004
|
"@clerk/nextjs": buildClerkMockSource,
|
|
954
|
-
"@clerk/clerk-react": buildClerkMockSource
|
|
1005
|
+
"@clerk/clerk-react": buildClerkMockSource,
|
|
1006
|
+
"convex/react": buildConvexReactMockSource,
|
|
1007
|
+
"@convex-dev/auth/react": buildConvexAuthMockSource
|
|
955
1008
|
};
|
|
956
1009
|
function isKnownMockPackage(pkg) {
|
|
957
1010
|
return pkg in BUILDERS;
|
|
@@ -1075,7 +1128,7 @@ function buildClerkMockSource(usedExports) {
|
|
|
1075
1128
|
const names = Array.from(
|
|
1076
1129
|
new Set(
|
|
1077
1130
|
usedExports.filter(
|
|
1078
|
-
(n) =>
|
|
1131
|
+
(n) => VALID_IDENT_RE3.test(n) && n !== "default" && !RESERVED_HELPER_NAMES2.has(n)
|
|
1079
1132
|
)
|
|
1080
1133
|
)
|
|
1081
1134
|
).sort();
|
|
@@ -1131,63 +1184,61 @@ function packageMockPlugin(options) {
|
|
|
1131
1184
|
|
|
1132
1185
|
// src/screenshot-service/utils/console-logs/console-logs.ts
|
|
1133
1186
|
async function captureConsoleLogs(storyId, theme = "light", storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
timestamp: Date.now()
|
|
1149
|
-
});
|
|
1150
|
-
});
|
|
1151
|
-
page.on("pageerror", (err) => {
|
|
1152
|
-
pageErrors.push(err.message);
|
|
1153
|
-
});
|
|
1154
|
-
page.on("response", (res) => {
|
|
1155
|
-
const status = res.status();
|
|
1156
|
-
if (status >= 400) {
|
|
1157
|
-
const req = res.request();
|
|
1158
|
-
failedRequests.push({
|
|
1159
|
-
url: res.url(),
|
|
1160
|
-
status,
|
|
1161
|
-
method: req.method(),
|
|
1162
|
-
resourceType: req.resourceType(),
|
|
1163
|
-
failure: null
|
|
1164
|
-
});
|
|
1165
|
-
}
|
|
1187
|
+
const browser = await getBrowser();
|
|
1188
|
+
const context = await browser.newContext({
|
|
1189
|
+
viewport: { width: VIEWPORT_WIDTH, height: VIEWPORT_HEIGHT }
|
|
1190
|
+
});
|
|
1191
|
+
const page = await context.newPage();
|
|
1192
|
+
const logs = [];
|
|
1193
|
+
const pageErrors = [];
|
|
1194
|
+
const failedRequests = [];
|
|
1195
|
+
page.on("console", (msg) => {
|
|
1196
|
+
const level = msg.type();
|
|
1197
|
+
logs.push({
|
|
1198
|
+
level: level === "warning" ? "warn" : level,
|
|
1199
|
+
text: msg.text(),
|
|
1200
|
+
timestamp: Date.now()
|
|
1166
1201
|
});
|
|
1167
|
-
|
|
1202
|
+
});
|
|
1203
|
+
page.on("pageerror", (err) => {
|
|
1204
|
+
pageErrors.push(err.message);
|
|
1205
|
+
});
|
|
1206
|
+
page.on("response", (res) => {
|
|
1207
|
+
const status = res.status();
|
|
1208
|
+
if (status >= 400) {
|
|
1209
|
+
const req = res.request();
|
|
1168
1210
|
failedRequests.push({
|
|
1169
|
-
url:
|
|
1170
|
-
status
|
|
1211
|
+
url: res.url(),
|
|
1212
|
+
status,
|
|
1171
1213
|
method: req.method(),
|
|
1172
1214
|
resourceType: req.resourceType(),
|
|
1173
|
-
failure:
|
|
1215
|
+
failure: null
|
|
1174
1216
|
});
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
page.on("requestfailed", (req) => {
|
|
1220
|
+
failedRequests.push({
|
|
1221
|
+
url: req.url(),
|
|
1222
|
+
status: null,
|
|
1223
|
+
method: req.method(),
|
|
1224
|
+
resourceType: req.resourceType(),
|
|
1225
|
+
failure: req.failure()?.errorText ?? "request failed"
|
|
1175
1226
|
});
|
|
1176
|
-
|
|
1227
|
+
});
|
|
1228
|
+
const url = `${storybookUrl}/iframe.html?id=${storyId}&viewMode=story&globals=theme:${theme}`;
|
|
1229
|
+
try {
|
|
1230
|
+
await page.goto(url, { timeout: timeoutMs });
|
|
1231
|
+
await page.waitForLoadState("domcontentloaded", { timeout: timeoutMs });
|
|
1232
|
+
await page.waitForLoadState("load", { timeout: timeoutMs });
|
|
1177
1233
|
try {
|
|
1178
|
-
await page.
|
|
1179
|
-
|
|
1180
|
-
await page.waitForLoadState("load", { timeout: timeoutMs });
|
|
1181
|
-
try {
|
|
1182
|
-
await page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
1183
|
-
} catch {
|
|
1184
|
-
}
|
|
1185
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
1186
|
-
return { logs, pageErrors, failedRequests, url };
|
|
1187
|
-
} finally {
|
|
1188
|
-
await context.close();
|
|
1234
|
+
await page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
1235
|
+
} catch {
|
|
1189
1236
|
}
|
|
1190
|
-
|
|
1237
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
1238
|
+
return { logs, pageErrors, failedRequests, url };
|
|
1239
|
+
} finally {
|
|
1240
|
+
await context.close();
|
|
1241
|
+
}
|
|
1191
1242
|
}
|
|
1192
1243
|
|
|
1193
1244
|
// src/soft-story-rerender/soft-story-rerender.ts
|
|
@@ -1378,17 +1429,6 @@ function findGitRoot(startPath) {
|
|
|
1378
1429
|
}
|
|
1379
1430
|
return null;
|
|
1380
1431
|
}
|
|
1381
|
-
function ifNoneMatchHits(header, etag) {
|
|
1382
|
-
if (!header) return false;
|
|
1383
|
-
return header.split(",").some((raw) => {
|
|
1384
|
-
const token = raw.trim();
|
|
1385
|
-
return token === etag || token === `W/${etag}`;
|
|
1386
|
-
});
|
|
1387
|
-
}
|
|
1388
|
-
function decideIndexResponse(body, ifNoneMatch) {
|
|
1389
|
-
const etag = `"${createHash("sha1").update(body).digest("hex")}"`;
|
|
1390
|
-
return { etag, notModified: ifNoneMatchHits(ifNoneMatch, etag) };
|
|
1391
|
-
}
|
|
1392
1432
|
var STORY_FILE_PATTERN = /\.stories\.(js|jsx|mjs|ts|tsx|mdx)$/;
|
|
1393
1433
|
function isStoryFile(file) {
|
|
1394
1434
|
return STORY_FILE_PATTERN.test(file);
|
|
@@ -1421,7 +1461,8 @@ function portFromArgv(argv) {
|
|
|
1421
1461
|
}
|
|
1422
1462
|
async function defaultFetchIndex(port) {
|
|
1423
1463
|
try {
|
|
1424
|
-
const res = await fetch(`http://localhost:${port}/index.json`, {
|
|
1464
|
+
const res = await fetch(`http://localhost:${port}/index.json?_t=${Date.now()}`, {
|
|
1465
|
+
cache: "no-store",
|
|
1425
1466
|
signal: AbortSignal.timeout(2e3)
|
|
1426
1467
|
});
|
|
1427
1468
|
if (!res.ok) return null;
|
|
@@ -1581,8 +1622,15 @@ function detectTailwindEntryCss(cwd) {
|
|
|
1581
1622
|
var serveMetadataAndScreenshots = (req, res, next) => {
|
|
1582
1623
|
if (req.url === "/onbook-index.json") {
|
|
1583
1624
|
console.log("[STORYBOOK_PLUGIN] Serving /onbook-index.json endpoint");
|
|
1625
|
+
const cacheBuster = Date.now();
|
|
1584
1626
|
console.log("[STORYBOOK_PLUGIN] Fetching http://localhost:6006/index.json");
|
|
1585
|
-
fetch(
|
|
1627
|
+
fetch(`http://localhost:6006/index.json?_t=${cacheBuster}`, {
|
|
1628
|
+
cache: "no-store",
|
|
1629
|
+
headers: {
|
|
1630
|
+
"Cache-Control": "no-cache",
|
|
1631
|
+
Pragma: "no-cache"
|
|
1632
|
+
}
|
|
1633
|
+
}).then((response) => {
|
|
1586
1634
|
console.log("[STORYBOOK_PLUGIN] Storybook index fetch response", {
|
|
1587
1635
|
status: response.status,
|
|
1588
1636
|
ok: response.ok,
|
|
@@ -1601,21 +1649,12 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1601
1649
|
entryCount: Object.keys(indexData.entries || {}).length,
|
|
1602
1650
|
hasMetadata: !!indexData.meta
|
|
1603
1651
|
});
|
|
1604
|
-
const body = JSON.stringify(indexData);
|
|
1605
|
-
const { etag, notModified } = decideIndexResponse(
|
|
1606
|
-
body,
|
|
1607
|
-
req.headers["if-none-match"]
|
|
1608
|
-
);
|
|
1609
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1610
|
-
res.setHeader("ETag", etag);
|
|
1611
|
-
res.setHeader("Cache-Control", "no-cache");
|
|
1612
|
-
if (notModified) {
|
|
1613
|
-
res.statusCode = 304;
|
|
1614
|
-
res.end();
|
|
1615
|
-
return;
|
|
1616
|
-
}
|
|
1617
1652
|
res.setHeader("Content-Type", "application/json");
|
|
1618
|
-
res.
|
|
1653
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1654
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
1655
|
+
res.setHeader("Pragma", "no-cache");
|
|
1656
|
+
res.setHeader("Expires", "0");
|
|
1657
|
+
res.end(JSON.stringify(indexData));
|
|
1619
1658
|
}).catch((error) => {
|
|
1620
1659
|
console.error("[STORYBOOK_PLUGIN] Failed to fetch/extend index.json", {
|
|
1621
1660
|
error: error instanceof Error ? error.message : String(error),
|
package/dist/preset/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { O as OnlookPluginOptions } from '../storybook-onlook-plugin-
|
|
1
|
+
import { O as OnlookPluginOptions } from '../storybook-onlook-plugin-J4qj72mN.js';
|
|
2
2
|
import 'vite';
|
|
3
3
|
|
|
4
4
|
declare function pluginOptionsFromPresetOptions(options: Record<string, unknown> | undefined): OnlookPluginOptions;
|