@onlook/storybook-plugin 0.4.0-beta.10 → 0.4.0-beta.12
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 +119 -29
- package/dist/index.d.ts +1 -1
- package/dist/index.js +256 -53
- package/dist/preset/index.d.ts +1 -1
- package/dist/preset/index.js +260 -54
- package/dist/screenshot-service/index.js +77 -5
- package/dist/{storybook-onlook-plugin-CigILdDb.d.ts → storybook-onlook-plugin-B9Eo_OIq.d.ts} +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
-
import
|
|
2
|
+
import path7, { dirname, join, relative } from 'path';
|
|
3
3
|
import crypto, { createHash } from 'crypto';
|
|
4
|
-
import
|
|
4
|
+
import fs5, { existsSync } from 'fs';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import generateModule from '@babel/generator';
|
|
7
7
|
import { parse } from '@babel/parser';
|
|
@@ -61,7 +61,7 @@ function envContainmentPlugin(options) {
|
|
|
61
61
|
const resolveOutcomes = /* @__PURE__ */ new Map();
|
|
62
62
|
const roots = /* @__PURE__ */ new Set([normalizeSlashes(process.cwd())]);
|
|
63
63
|
const matchTarget = (absPath) => {
|
|
64
|
-
const norm = normalizeSlashes(
|
|
64
|
+
const norm = normalizeSlashes(path7.normalize(absPath));
|
|
65
65
|
for (const rel of byModule.keys()) {
|
|
66
66
|
for (const root of roots) {
|
|
67
67
|
const target = `${root}/${rel}`;
|
|
@@ -96,10 +96,10 @@ function envContainmentPlugin(options) {
|
|
|
96
96
|
if (source.startsWith("\0")) return null;
|
|
97
97
|
const cleaned = stripQuery(source);
|
|
98
98
|
let direct = null;
|
|
99
|
-
if (
|
|
99
|
+
if (path7.isAbsolute(cleaned)) {
|
|
100
100
|
direct = cleaned;
|
|
101
101
|
} else if (importer && (cleaned.startsWith("./") || cleaned.startsWith("../"))) {
|
|
102
|
-
direct =
|
|
102
|
+
direct = path7.resolve(path7.dirname(stripQuery(importer)), cleaned);
|
|
103
103
|
}
|
|
104
104
|
if (direct) {
|
|
105
105
|
const rel = matchTarget(direct);
|
|
@@ -108,7 +108,7 @@ function envContainmentPlugin(options) {
|
|
|
108
108
|
}
|
|
109
109
|
if (!specifierMightMatch(cleaned)) return null;
|
|
110
110
|
if (typeof this.resolve !== "function") return null;
|
|
111
|
-
const importerDir = importer ?
|
|
111
|
+
const importerDir = importer ? path7.dirname(stripQuery(importer)) : "";
|
|
112
112
|
const outcomeKey = `${importerDir}|${source}`;
|
|
113
113
|
const memoized = resolveOutcomes.get(outcomeKey);
|
|
114
114
|
if (memoized !== void 0) return memoized;
|
|
@@ -328,7 +328,7 @@ function componentLocPlugin(options = {}) {
|
|
|
328
328
|
sourceFilename: filepath
|
|
329
329
|
});
|
|
330
330
|
let mutated = false;
|
|
331
|
-
const relativePath =
|
|
331
|
+
const relativePath = path7.relative(root, filepath);
|
|
332
332
|
traverse(ast, {
|
|
333
333
|
JSXElement(nodePath) {
|
|
334
334
|
const opening = nodePath.node.openingElement;
|
|
@@ -387,9 +387,9 @@ function fastRefreshTolerantExportsPlugin() {
|
|
|
387
387
|
}
|
|
388
388
|
};
|
|
389
389
|
}
|
|
390
|
-
var CACHE_DIR =
|
|
391
|
-
var SCREENSHOTS_DIR =
|
|
392
|
-
var MANIFEST_PATH =
|
|
390
|
+
var CACHE_DIR = path7.join(process.cwd(), ".storybook-cache");
|
|
391
|
+
var SCREENSHOTS_DIR = path7.join(CACHE_DIR, "screenshots");
|
|
392
|
+
var MANIFEST_PATH = path7.join(CACHE_DIR, "manifest.json");
|
|
393
393
|
var VIEWPORT_WIDTH = 1920;
|
|
394
394
|
var VIEWPORT_HEIGHT = 1080;
|
|
395
395
|
var MIN_COMPONENT_WIDTH = 420;
|
|
@@ -397,30 +397,30 @@ var MIN_COMPONENT_HEIGHT = 280;
|
|
|
397
397
|
|
|
398
398
|
// src/utils/fileSystem/fileSystem.ts
|
|
399
399
|
function ensureCacheDirectories() {
|
|
400
|
-
if (!
|
|
401
|
-
|
|
400
|
+
if (!fs5.existsSync(CACHE_DIR)) {
|
|
401
|
+
fs5.mkdirSync(CACHE_DIR, { recursive: true });
|
|
402
402
|
}
|
|
403
|
-
if (!
|
|
404
|
-
|
|
403
|
+
if (!fs5.existsSync(SCREENSHOTS_DIR)) {
|
|
404
|
+
fs5.mkdirSync(SCREENSHOTS_DIR, { recursive: true });
|
|
405
405
|
}
|
|
406
406
|
}
|
|
407
407
|
function computeFileHash(filePath) {
|
|
408
|
-
if (!
|
|
408
|
+
if (!fs5.existsSync(filePath)) {
|
|
409
409
|
return "";
|
|
410
410
|
}
|
|
411
|
-
const content =
|
|
411
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
412
412
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
413
413
|
}
|
|
414
414
|
function loadManifest() {
|
|
415
|
-
if (
|
|
416
|
-
const content =
|
|
415
|
+
if (fs5.existsSync(MANIFEST_PATH)) {
|
|
416
|
+
const content = fs5.readFileSync(MANIFEST_PATH, "utf-8");
|
|
417
417
|
return JSON.parse(content);
|
|
418
418
|
}
|
|
419
419
|
return { stories: {} };
|
|
420
420
|
}
|
|
421
421
|
function saveManifest(manifest) {
|
|
422
422
|
ensureCacheDirectories();
|
|
423
|
-
|
|
423
|
+
fs5.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
424
424
|
}
|
|
425
425
|
function updateManifest(storyId, sourcePath, fileHash, boundingBox) {
|
|
426
426
|
const manifest = loadManifest();
|
|
@@ -509,11 +509,76 @@ async function getBrowser() {
|
|
|
509
509
|
}
|
|
510
510
|
return browserPromise;
|
|
511
511
|
}
|
|
512
|
+
function classifyPreviewSnapshot(s) {
|
|
513
|
+
if (s.text.includes("Sorry, something went wrong")) return "errored";
|
|
514
|
+
if (s.text.includes("Couldn't find story")) return "errored";
|
|
515
|
+
if (s.hasContainedCard) return "contained";
|
|
516
|
+
if (s.renderStateAttr === "contained") return "contained";
|
|
517
|
+
if (s.renderStateAttr === "rendered") return "rendered";
|
|
518
|
+
if (s.text.length > 0) return "rendered";
|
|
519
|
+
if (s.hasSizedElement) return "rendered";
|
|
520
|
+
return "pending";
|
|
521
|
+
}
|
|
522
|
+
async function detectRenderState(page, opts = {}) {
|
|
523
|
+
const pollMs = opts.pollMs ?? 250;
|
|
524
|
+
const settleMs = opts.settleMs ?? 4e3;
|
|
525
|
+
const blankStablePolls = opts.blankStablePolls ?? 6;
|
|
526
|
+
const hardCapMs = opts.hardCapMs ?? 2e4;
|
|
527
|
+
const now = opts.now ?? Date.now;
|
|
528
|
+
const start = now();
|
|
529
|
+
let emptyStreak = 0;
|
|
530
|
+
const snapshot = () => page.evaluate(
|
|
531
|
+
({ stateAttr, cardAttr }) => {
|
|
532
|
+
const root = document.getElementById("storybook-root") ?? document.body;
|
|
533
|
+
const text = (root?.textContent ?? "").trim();
|
|
534
|
+
let hasSizedElement = false;
|
|
535
|
+
if (root) {
|
|
536
|
+
const all = root.querySelectorAll("*");
|
|
537
|
+
for (let i = 0; i < all.length; i++) {
|
|
538
|
+
const rect = all[i]?.getBoundingClientRect();
|
|
539
|
+
if (rect && rect.width > 0 && rect.height > 0) {
|
|
540
|
+
hasSizedElement = true;
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return {
|
|
546
|
+
text,
|
|
547
|
+
hasContainedCard: Boolean(root?.querySelector(`[${cardAttr}]`)),
|
|
548
|
+
renderStateAttr: root?.getAttribute(stateAttr) ?? null,
|
|
549
|
+
hasSizedElement
|
|
550
|
+
};
|
|
551
|
+
},
|
|
552
|
+
{ stateAttr: ONLOOK_RENDER_STATE_ATTR, cardAttr: ONLOOK_CONTAINED_CARD_ATTR }
|
|
553
|
+
);
|
|
554
|
+
while (true) {
|
|
555
|
+
let state;
|
|
556
|
+
let threw = false;
|
|
557
|
+
try {
|
|
558
|
+
state = classifyPreviewSnapshot(await snapshot());
|
|
559
|
+
} catch {
|
|
560
|
+
state = "pending";
|
|
561
|
+
threw = true;
|
|
562
|
+
}
|
|
563
|
+
if (state === "rendered" || state === "contained" || state === "errored") {
|
|
564
|
+
return state;
|
|
565
|
+
}
|
|
566
|
+
const elapsed = now() - start;
|
|
567
|
+
if (threw) {
|
|
568
|
+
emptyStreak = 0;
|
|
569
|
+
} else if (elapsed >= settleMs) {
|
|
570
|
+
emptyStreak += 1;
|
|
571
|
+
if (emptyStreak >= blankStablePolls) return "blank";
|
|
572
|
+
}
|
|
573
|
+
if (elapsed >= hardCapMs) return "timedOut";
|
|
574
|
+
await page.waitForTimeout(pollMs);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
512
577
|
function getScreenshotPath(storyId, theme) {
|
|
513
|
-
const storyDir =
|
|
514
|
-
return
|
|
578
|
+
const storyDir = path7.join(SCREENSHOTS_DIR, storyId);
|
|
579
|
+
return path7.join(storyDir, `${theme}.png`);
|
|
515
580
|
}
|
|
516
|
-
async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, height = VIEWPORT_HEIGHT, storybookUrl = "http://localhost:6006",
|
|
581
|
+
async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, height = VIEWPORT_HEIGHT, storybookUrl = "http://localhost:6006", loadTimeoutMs = 9e4, renderTimeoutMs = 2e4) {
|
|
517
582
|
const browser = await getBrowser();
|
|
518
583
|
const context = await browser.newContext({
|
|
519
584
|
viewport: { width, height },
|
|
@@ -522,13 +587,14 @@ async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, h
|
|
|
522
587
|
const page = await context.newPage();
|
|
523
588
|
try {
|
|
524
589
|
const url = `${storybookUrl}/iframe.html?id=${storyId}&viewMode=story&globals=theme:${theme}`;
|
|
525
|
-
await page.goto(url, { timeout:
|
|
526
|
-
await page.waitForLoadState("domcontentloaded", { timeout:
|
|
527
|
-
await page.waitForLoadState("load", { timeout:
|
|
590
|
+
await page.goto(url, { timeout: loadTimeoutMs });
|
|
591
|
+
await page.waitForLoadState("domcontentloaded", { timeout: loadTimeoutMs });
|
|
592
|
+
await page.waitForLoadState("load", { timeout: loadTimeoutMs });
|
|
528
593
|
try {
|
|
529
594
|
await page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
530
595
|
} catch {
|
|
531
596
|
}
|
|
597
|
+
const renderState = await detectRenderState(page, { hardCapMs: renderTimeoutMs });
|
|
532
598
|
await page.evaluate(() => document.fonts.ready);
|
|
533
599
|
try {
|
|
534
600
|
await page.evaluate(async () => {
|
|
@@ -590,7 +656,7 @@ async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, h
|
|
|
590
656
|
} else {
|
|
591
657
|
screenshotBuffer = await page.screenshot({ type: "png" });
|
|
592
658
|
}
|
|
593
|
-
return { buffer: screenshotBuffer, boundingBox: resultBoundingBox };
|
|
659
|
+
return { buffer: screenshotBuffer, boundingBox: resultBoundingBox, renderState };
|
|
594
660
|
} finally {
|
|
595
661
|
await context.close();
|
|
596
662
|
}
|
|
@@ -598,9 +664,9 @@ async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, h
|
|
|
598
664
|
async function generateScreenshot(storyId, theme, storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
599
665
|
try {
|
|
600
666
|
ensureCacheDirectories();
|
|
601
|
-
const storyDir =
|
|
602
|
-
if (!
|
|
603
|
-
|
|
667
|
+
const storyDir = path7.join(SCREENSHOTS_DIR, storyId);
|
|
668
|
+
if (!fs5.existsSync(storyDir)) {
|
|
669
|
+
fs5.mkdirSync(storyDir, { recursive: true });
|
|
604
670
|
}
|
|
605
671
|
const screenshotPath = getScreenshotPath(storyId, theme);
|
|
606
672
|
const { buffer, boundingBox } = await captureScreenshotBuffer(
|
|
@@ -611,7 +677,7 @@ async function generateScreenshot(storyId, theme, storybookUrl = "http://localho
|
|
|
611
677
|
storybookUrl,
|
|
612
678
|
timeoutMs
|
|
613
679
|
);
|
|
614
|
-
|
|
680
|
+
fs5.writeFileSync(screenshotPath, buffer);
|
|
615
681
|
return { path: screenshotPath, boundingBox };
|
|
616
682
|
} catch (error) {
|
|
617
683
|
console.error(`Error generating screenshot for ${storyId} (${theme}):`, error);
|
|
@@ -719,7 +785,7 @@ async function fetchStorybookIndex() {
|
|
|
719
785
|
}
|
|
720
786
|
function getStoriesForFile(filePath) {
|
|
721
787
|
if (!cachedIndex) return [];
|
|
722
|
-
const fileName =
|
|
788
|
+
const fileName = path7.basename(filePath);
|
|
723
789
|
return Object.values(cachedIndex.entries).filter((entry) => entry.type === "story" && entry.importPath.endsWith(fileName)).map((entry) => entry.id);
|
|
724
790
|
}
|
|
725
791
|
async function regenerateScreenshotsForFiles(files) {
|
|
@@ -795,6 +861,7 @@ async function captureConsoleLogs(storyId, theme = "light", storybookUrl = "http
|
|
|
795
861
|
const page = await context.newPage();
|
|
796
862
|
const logs = [];
|
|
797
863
|
const pageErrors = [];
|
|
864
|
+
const failedRequests = [];
|
|
798
865
|
page.on("console", (msg) => {
|
|
799
866
|
const level = msg.type();
|
|
800
867
|
logs.push({
|
|
@@ -806,6 +873,28 @@ async function captureConsoleLogs(storyId, theme = "light", storybookUrl = "http
|
|
|
806
873
|
page.on("pageerror", (err) => {
|
|
807
874
|
pageErrors.push(err.message);
|
|
808
875
|
});
|
|
876
|
+
page.on("response", (res) => {
|
|
877
|
+
const status = res.status();
|
|
878
|
+
if (status >= 400) {
|
|
879
|
+
const req = res.request();
|
|
880
|
+
failedRequests.push({
|
|
881
|
+
url: res.url(),
|
|
882
|
+
status,
|
|
883
|
+
method: req.method(),
|
|
884
|
+
resourceType: req.resourceType(),
|
|
885
|
+
failure: null
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
page.on("requestfailed", (req) => {
|
|
890
|
+
failedRequests.push({
|
|
891
|
+
url: req.url(),
|
|
892
|
+
status: null,
|
|
893
|
+
method: req.method(),
|
|
894
|
+
resourceType: req.resourceType(),
|
|
895
|
+
failure: req.failure()?.errorText ?? "request failed"
|
|
896
|
+
});
|
|
897
|
+
});
|
|
809
898
|
const url = `${storybookUrl}/iframe.html?id=${storyId}&viewMode=story&globals=theme:${theme}`;
|
|
810
899
|
try {
|
|
811
900
|
await page.goto(url, { timeout: timeoutMs });
|
|
@@ -816,7 +905,7 @@ async function captureConsoleLogs(storyId, theme = "light", storybookUrl = "http
|
|
|
816
905
|
} catch {
|
|
817
906
|
}
|
|
818
907
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
819
|
-
return { logs, pageErrors, url };
|
|
908
|
+
return { logs, pageErrors, failedRequests, url };
|
|
820
909
|
} finally {
|
|
821
910
|
await context.close();
|
|
822
911
|
}
|
|
@@ -891,13 +980,115 @@ function softStoryRerenderPlugin() {
|
|
|
891
980
|
preview.onStoriesChanged({ importFn: newModule.importFn });
|
|
892
981
|
}
|
|
893
982
|
});`;
|
|
894
|
-
const out = code.replace(pattern, (_match, quote,
|
|
895
|
-
return replacement.replace("$QUOTE$$PATH$$QUOTE$", `${quote}${
|
|
983
|
+
const out = code.replace(pattern, (_match, quote, path8) => {
|
|
984
|
+
return replacement.replace("$QUOTE$$PATH$$QUOTE$", `${quote}${path8}${quote}`);
|
|
896
985
|
});
|
|
897
986
|
return out === code ? null : { code: out, map: null };
|
|
898
987
|
}
|
|
899
988
|
};
|
|
900
989
|
}
|
|
990
|
+
var TSCONFIG_CANDIDATES = ["tsconfig.json", "tsconfig.app.json", "tsconfig.base.json"];
|
|
991
|
+
function resolveTsconfigAliases(root) {
|
|
992
|
+
try {
|
|
993
|
+
const config = readTsconfigAliasConfig(root);
|
|
994
|
+
if (!config) return [];
|
|
995
|
+
const base = path7.resolve(root, config.baseUrl ?? ".");
|
|
996
|
+
const aliases = [];
|
|
997
|
+
for (const [key, targets] of Object.entries(config.paths)) {
|
|
998
|
+
const target = targets[0];
|
|
999
|
+
if (!target) continue;
|
|
1000
|
+
if (key.endsWith("/*") && target.endsWith("/*")) {
|
|
1001
|
+
const findPrefix = key.slice(0, -1);
|
|
1002
|
+
const targetDir = path7.resolve(base, target.slice(0, -2));
|
|
1003
|
+
aliases.push({
|
|
1004
|
+
find: new RegExp(`^${escapeRegExp(findPrefix)}`),
|
|
1005
|
+
replacement: `${targetDir}/`
|
|
1006
|
+
});
|
|
1007
|
+
} else if (!key.includes("*") && !target.includes("*")) {
|
|
1008
|
+
aliases.push({
|
|
1009
|
+
find: new RegExp(`^${escapeRegExp(key)}$`),
|
|
1010
|
+
replacement: path7.resolve(base, target)
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
return aliases.sort((a, b) => b.find.source.length - a.find.source.length);
|
|
1015
|
+
} catch {
|
|
1016
|
+
return [];
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
function readTsconfigAliasConfig(root) {
|
|
1020
|
+
for (const candidate of TSCONFIG_CANDIDATES) {
|
|
1021
|
+
const abs = path7.join(root, candidate);
|
|
1022
|
+
let text;
|
|
1023
|
+
try {
|
|
1024
|
+
text = fs5.readFileSync(abs, "utf-8");
|
|
1025
|
+
} catch {
|
|
1026
|
+
continue;
|
|
1027
|
+
}
|
|
1028
|
+
let parsed;
|
|
1029
|
+
try {
|
|
1030
|
+
parsed = JSON.parse(stripJsonComments(text));
|
|
1031
|
+
} catch {
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
const paths = parsed.compilerOptions?.paths;
|
|
1035
|
+
if (paths && typeof paths === "object" && Object.keys(paths).length > 0) {
|
|
1036
|
+
const baseUrl = parsed.compilerOptions?.baseUrl;
|
|
1037
|
+
return typeof baseUrl === "string" ? { baseUrl, paths } : { paths };
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
return null;
|
|
1041
|
+
}
|
|
1042
|
+
function escapeRegExp(value) {
|
|
1043
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1044
|
+
}
|
|
1045
|
+
function stripJsonComments(text) {
|
|
1046
|
+
let out = "";
|
|
1047
|
+
let inString = false;
|
|
1048
|
+
let inLine = false;
|
|
1049
|
+
let inBlock = false;
|
|
1050
|
+
for (let i = 0; i < text.length; i++) {
|
|
1051
|
+
const c = text.charAt(i);
|
|
1052
|
+
const next = text.charAt(i + 1);
|
|
1053
|
+
if (inLine) {
|
|
1054
|
+
if (c === "\n") {
|
|
1055
|
+
inLine = false;
|
|
1056
|
+
out += c;
|
|
1057
|
+
}
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
if (inBlock) {
|
|
1061
|
+
if (c === "*" && next === "/") {
|
|
1062
|
+
inBlock = false;
|
|
1063
|
+
i++;
|
|
1064
|
+
}
|
|
1065
|
+
continue;
|
|
1066
|
+
}
|
|
1067
|
+
if (inString) {
|
|
1068
|
+
out += c;
|
|
1069
|
+
if (c === "\\") {
|
|
1070
|
+
out += next;
|
|
1071
|
+
i++;
|
|
1072
|
+
} else if (c === '"') {
|
|
1073
|
+
inString = false;
|
|
1074
|
+
}
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
if (c === '"') {
|
|
1078
|
+
inString = true;
|
|
1079
|
+
out += c;
|
|
1080
|
+
} else if (c === "/" && next === "/") {
|
|
1081
|
+
inLine = true;
|
|
1082
|
+
i++;
|
|
1083
|
+
} else if (c === "/" && next === "*") {
|
|
1084
|
+
inBlock = true;
|
|
1085
|
+
i++;
|
|
1086
|
+
} else {
|
|
1087
|
+
out += c;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
return out;
|
|
1091
|
+
}
|
|
901
1092
|
function findGitRoot(startPath) {
|
|
902
1093
|
let currentPath = startPath;
|
|
903
1094
|
while (currentPath !== dirname(currentPath)) {
|
|
@@ -924,8 +1115,8 @@ var manifestCache = null;
|
|
|
924
1115
|
var manifestHash = null;
|
|
925
1116
|
function readManifestFromDisk(filePath) {
|
|
926
1117
|
try {
|
|
927
|
-
if (!
|
|
928
|
-
const raw =
|
|
1118
|
+
if (!fs5.existsSync(filePath)) return null;
|
|
1119
|
+
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
929
1120
|
const hash = createHash("sha1").update(raw).digest("hex");
|
|
930
1121
|
const manifest = JSON.parse(raw);
|
|
931
1122
|
return { manifest, hash };
|
|
@@ -968,10 +1159,10 @@ var TAILWIND_ENTRY_CANDIDATES = [
|
|
|
968
1159
|
var TAILWIND_DIRECTIVE_PATTERN = /@import\s+['"](?:tailwindcss|tailwindcss\/.+)['"]|@tailwind\s+(?:base|components|utilities)/;
|
|
969
1160
|
function detectTailwindEntryCss(cwd) {
|
|
970
1161
|
for (const rel of TAILWIND_ENTRY_CANDIDATES) {
|
|
971
|
-
const abs =
|
|
1162
|
+
const abs = path7.join(cwd, rel);
|
|
972
1163
|
try {
|
|
973
|
-
if (!
|
|
974
|
-
const head =
|
|
1164
|
+
if (!fs5.existsSync(abs)) continue;
|
|
1165
|
+
const head = fs5.readFileSync(abs, "utf-8").slice(0, 2048);
|
|
975
1166
|
if (TAILWIND_DIRECTIVE_PATTERN.test(head)) return abs;
|
|
976
1167
|
} catch {
|
|
977
1168
|
}
|
|
@@ -1046,9 +1237,11 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1046
1237
|
res.end(JSON.stringify({ error: 'theme must be "light" or "dark"' }));
|
|
1047
1238
|
return;
|
|
1048
1239
|
}
|
|
1049
|
-
captureScreenshotBuffer(storyId, theme, width, height).then(({ buffer }) => {
|
|
1240
|
+
captureScreenshotBuffer(storyId, theme, width, height).then(({ buffer, renderState }) => {
|
|
1050
1241
|
res.setHeader("Content-Type", "image/png");
|
|
1051
1242
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1243
|
+
res.setHeader("Access-Control-Expose-Headers", "x-onlook-render-state");
|
|
1244
|
+
res.setHeader("x-onlook-render-state", renderState);
|
|
1052
1245
|
res.setHeader("Cache-Control", "no-cache");
|
|
1053
1246
|
res.end(buffer);
|
|
1054
1247
|
}).catch((error) => {
|
|
@@ -1099,7 +1292,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1099
1292
|
return;
|
|
1100
1293
|
}
|
|
1101
1294
|
if (req.url?.startsWith("/screenshots/")) {
|
|
1102
|
-
const screenshotPath =
|
|
1295
|
+
const screenshotPath = path7.join(
|
|
1103
1296
|
process.cwd(),
|
|
1104
1297
|
".storybook-cache",
|
|
1105
1298
|
req.url.replace("/screenshots/", "screenshots/")
|
|
@@ -1108,11 +1301,11 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1108
1301
|
const storyId = urlParts[0];
|
|
1109
1302
|
const themeFile = urlParts[1];
|
|
1110
1303
|
const theme = themeFile?.replace(".png", "");
|
|
1111
|
-
if (
|
|
1304
|
+
if (fs5.existsSync(screenshotPath)) {
|
|
1112
1305
|
res.setHeader("Content-Type", "image/png");
|
|
1113
1306
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1114
1307
|
res.setHeader("Cache-Control", "public, max-age=3600");
|
|
1115
|
-
|
|
1308
|
+
fs5.createReadStream(screenshotPath).pipe(res);
|
|
1116
1309
|
return;
|
|
1117
1310
|
}
|
|
1118
1311
|
if (storyId && theme && (theme === "light" || theme === "dark")) {
|
|
@@ -1120,16 +1313,16 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1120
1313
|
`[STORYBOOK_PLUGIN] Generating screenshot on-demand: ${storyId}/${theme}`
|
|
1121
1314
|
);
|
|
1122
1315
|
captureScreenshotBuffer(storyId, theme).then(({ buffer }) => {
|
|
1123
|
-
const storyDir =
|
|
1316
|
+
const storyDir = path7.join(
|
|
1124
1317
|
process.cwd(),
|
|
1125
1318
|
".storybook-cache",
|
|
1126
1319
|
"screenshots",
|
|
1127
1320
|
storyId
|
|
1128
1321
|
);
|
|
1129
|
-
if (!
|
|
1130
|
-
|
|
1322
|
+
if (!fs5.existsSync(storyDir)) {
|
|
1323
|
+
fs5.mkdirSync(storyDir, { recursive: true });
|
|
1131
1324
|
}
|
|
1132
|
-
|
|
1325
|
+
fs5.writeFileSync(screenshotPath, buffer);
|
|
1133
1326
|
res.setHeader("Content-Type", "image/png");
|
|
1134
1327
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1135
1328
|
res.setHeader("Cache-Control", "public, max-age=3600");
|
|
@@ -1203,10 +1396,20 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1203
1396
|
repoRoot,
|
|
1204
1397
|
hmr: hmrOverride ?? null
|
|
1205
1398
|
});
|
|
1399
|
+
const aliasEntries = options.resolveAliases === false ? [] : resolveTsconfigAliases(process.cwd());
|
|
1400
|
+
if (aliasEntries.length > 0) {
|
|
1401
|
+
console.log("[STORYBOOK_PLUGIN] Resolved tsconfig path aliases", {
|
|
1402
|
+
count: aliasEntries.length,
|
|
1403
|
+
aliases: aliasEntries.map((a) => a.find.source)
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1206
1406
|
const mainPlugin = {
|
|
1207
1407
|
name: "storybook-onlook-plugin",
|
|
1208
1408
|
config() {
|
|
1209
1409
|
return {
|
|
1410
|
+
...aliasEntries.length > 0 && {
|
|
1411
|
+
resolve: { alias: aliasEntries }
|
|
1412
|
+
},
|
|
1210
1413
|
server: {
|
|
1211
1414
|
// HMR override applies only when the caller explicitly opts in
|
|
1212
1415
|
// (typically: Storybook fronted over HTTPS by a reverse proxy).
|
|
@@ -1227,7 +1430,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1227
1430
|
configureServer(server) {
|
|
1228
1431
|
console.log("[STORYBOOK_PLUGIN] Configuring server middleware");
|
|
1229
1432
|
server.middlewares.use(serveMetadataAndScreenshots);
|
|
1230
|
-
const cachedManifestPath =
|
|
1433
|
+
const cachedManifestPath = path7.join(
|
|
1231
1434
|
process.cwd(),
|
|
1232
1435
|
".storybook-cache",
|
|
1233
1436
|
"manifest.json"
|
|
@@ -1246,7 +1449,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1246
1449
|
server.watcher.on("change", scheduleRefresh);
|
|
1247
1450
|
const storyGlobs = options.storyGlobs ?? ["**/*.stories.@(js|jsx|mjs|ts|tsx|mdx)"];
|
|
1248
1451
|
const resolvedStoryGlobs = storyGlobs.map(
|
|
1249
|
-
(glob) =>
|
|
1452
|
+
(glob) => path7.isAbsolute(glob) ? glob : path7.join(process.cwd(), glob)
|
|
1250
1453
|
);
|
|
1251
1454
|
if (resolvedStoryGlobs.length > 0) {
|
|
1252
1455
|
server.watcher.add(resolvedStoryGlobs);
|
|
@@ -1257,7 +1460,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1257
1460
|
const tailwindEntryPath = (() => {
|
|
1258
1461
|
if (options.tailwindEntryCss === false) return null;
|
|
1259
1462
|
if (typeof options.tailwindEntryCss === "string") {
|
|
1260
|
-
return
|
|
1463
|
+
return path7.isAbsolute(options.tailwindEntryCss) ? options.tailwindEntryCss : path7.join(process.cwd(), options.tailwindEntryCss);
|
|
1261
1464
|
}
|
|
1262
1465
|
return detectTailwindEntryCss(process.cwd());
|
|
1263
1466
|
})();
|
|
@@ -1273,13 +1476,13 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1273
1476
|
});
|
|
1274
1477
|
server.watcher.on("add", (file) => {
|
|
1275
1478
|
if (!/\.tsx?$/.test(file)) return;
|
|
1276
|
-
const dir =
|
|
1479
|
+
const dir = path7.dirname(file);
|
|
1277
1480
|
const isNewDir = !seenDirs.has(dir);
|
|
1278
1481
|
seenDirs.add(dir);
|
|
1279
1482
|
if (!watcherReady || !isNewDir) return;
|
|
1280
1483
|
try {
|
|
1281
1484
|
const now = /* @__PURE__ */ new Date();
|
|
1282
|
-
|
|
1485
|
+
fs5.utimesSync(tailwindEntryPath, now, now);
|
|
1283
1486
|
console.log("[STORYBOOK_PLUGIN] Tailwind rescan for new dir", {
|
|
1284
1487
|
dir,
|
|
1285
1488
|
entry: tailwindEntryPath
|
|
@@ -1304,7 +1507,7 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1304
1507
|
configurePreviewServer(server) {
|
|
1305
1508
|
console.log("[STORYBOOK_PLUGIN] Configuring preview server middleware");
|
|
1306
1509
|
server.middlewares.use(serveMetadataAndScreenshots);
|
|
1307
|
-
refreshManifest(
|
|
1510
|
+
refreshManifest(path7.join(process.cwd(), ".storybook-cache", "manifest.json"));
|
|
1308
1511
|
},
|
|
1309
1512
|
handleHotUpdate: handleStoryFileChange
|
|
1310
1513
|
};
|
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-B9Eo_OIq.js';
|
|
2
2
|
import 'vite';
|
|
3
3
|
|
|
4
4
|
declare function pluginOptionsFromPresetOptions(options: Record<string, unknown> | undefined): OnlookPluginOptions;
|