@onlook/storybook-plugin 0.3.1 → 0.3.2-beta.1
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/README.md +1 -1
- package/dist/cli/index.js +160 -35
- package/dist/index.d.ts +11 -1
- package/dist/index.js +97310 -464
- package/dist/screenshot-service/index.js +104 -23
- package/dist/screenshot-service/utils/browser/index.js +2 -1
- package/package.json +4 -3
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
1
2
|
import crypto from 'crypto';
|
|
2
3
|
import fs from 'fs';
|
|
3
4
|
import path from 'path';
|
|
4
5
|
import { chromium } from 'playwright';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
globalThis.require = createRequire(import.meta.url);
|
|
7
8
|
var CACHE_DIR = path.join(process.cwd(), ".storybook-cache");
|
|
8
9
|
var SCREENSHOTS_DIR = path.join(CACHE_DIR, "screenshots");
|
|
9
10
|
var MANIFEST_PATH = path.join(CACHE_DIR, "manifest.json");
|
|
@@ -93,20 +94,27 @@ async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, h
|
|
|
93
94
|
await page.goto(url, { timeout: timeoutMs });
|
|
94
95
|
await page.waitForLoadState("domcontentloaded", { timeout: timeoutMs });
|
|
95
96
|
await page.waitForLoadState("load", { timeout: timeoutMs });
|
|
96
|
-
|
|
97
|
+
try {
|
|
98
|
+
await page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
97
101
|
await page.evaluate(() => document.fonts.ready);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
102
|
+
try {
|
|
103
|
+
await page.evaluate(async () => {
|
|
104
|
+
const images = document.querySelectorAll("img");
|
|
105
|
+
await Promise.all(
|
|
106
|
+
Array.from(images).map((img) => {
|
|
107
|
+
if (img.complete) return Promise.resolve();
|
|
108
|
+
return new Promise((resolve) => {
|
|
109
|
+
img.addEventListener("load", resolve);
|
|
110
|
+
img.addEventListener("error", resolve);
|
|
111
|
+
setTimeout(resolve, 3e3);
|
|
112
|
+
});
|
|
113
|
+
})
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
110
118
|
const contentBounds = await page.evaluate(() => {
|
|
111
119
|
const root = document.querySelector("#storybook-root");
|
|
112
120
|
if (!root) return null;
|
|
@@ -181,6 +189,33 @@ async function generateScreenshot(storyId, theme, storybookUrl = "http://localho
|
|
|
181
189
|
}
|
|
182
190
|
|
|
183
191
|
// src/screenshot-service/screenshot-service.ts
|
|
192
|
+
var StoryTimeoutError = class extends Error {
|
|
193
|
+
constructor(storyId, timeoutMs) {
|
|
194
|
+
super(`Story ${storyId} timed out after ${timeoutMs / 1e3}s`);
|
|
195
|
+
this.storyId = storyId;
|
|
196
|
+
this.timeoutMs = timeoutMs;
|
|
197
|
+
this.name = "StoryTimeoutError";
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
async function captureBothThemes(storyId, storybookUrl, timeoutMs, storyTimeoutMs) {
|
|
201
|
+
let timer;
|
|
202
|
+
try {
|
|
203
|
+
return await Promise.race([
|
|
204
|
+
Promise.all([
|
|
205
|
+
generateScreenshot(storyId, "light", storybookUrl, timeoutMs),
|
|
206
|
+
generateScreenshot(storyId, "dark", storybookUrl, timeoutMs)
|
|
207
|
+
]),
|
|
208
|
+
new Promise((_, reject) => {
|
|
209
|
+
timer = setTimeout(
|
|
210
|
+
() => reject(new StoryTimeoutError(storyId, storyTimeoutMs)),
|
|
211
|
+
storyTimeoutMs
|
|
212
|
+
);
|
|
213
|
+
})
|
|
214
|
+
]);
|
|
215
|
+
} finally {
|
|
216
|
+
if (timer) clearTimeout(timer);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
184
219
|
async function generateAllScreenshots(stories, storybookUrl = "http://localhost:6006", concurrency = 10, offset = 0, total, skipExisting = false, timeoutMs = 3e4) {
|
|
185
220
|
const displayTotal = total ?? offset + stories.length;
|
|
186
221
|
console.log(
|
|
@@ -191,6 +226,7 @@ async function generateAllScreenshots(stories, storybookUrl = "http://localhost:
|
|
|
191
226
|
for (let i = 0; i < stories.length; i += BATCH_SIZE) {
|
|
192
227
|
batches.push(stories.slice(i, i + BATCH_SIZE));
|
|
193
228
|
}
|
|
229
|
+
const storyTimeout = timeoutMs * 2 + 1e4;
|
|
194
230
|
let completed = 0;
|
|
195
231
|
for (const batch of batches) {
|
|
196
232
|
await Promise.all(
|
|
@@ -201,19 +237,64 @@ async function generateAllScreenshots(stories, storybookUrl = "http://localhost:
|
|
|
201
237
|
console.log(`[${absoluteIndex2}/${displayTotal}] Skipped (exists) ${story.id}`);
|
|
202
238
|
return;
|
|
203
239
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
240
|
+
let lightResult = null;
|
|
241
|
+
let darkResult = null;
|
|
242
|
+
let lastError;
|
|
243
|
+
const startedAt = Date.now();
|
|
244
|
+
let attempts = 0;
|
|
245
|
+
const maxAttempts = 2;
|
|
246
|
+
while (attempts < maxAttempts) {
|
|
247
|
+
attempts++;
|
|
248
|
+
try {
|
|
249
|
+
[lightResult, darkResult] = await captureBothThemes(
|
|
250
|
+
story.id,
|
|
251
|
+
storybookUrl,
|
|
252
|
+
timeoutMs,
|
|
253
|
+
storyTimeout
|
|
254
|
+
);
|
|
255
|
+
lastError = void 0;
|
|
256
|
+
break;
|
|
257
|
+
} catch (error) {
|
|
258
|
+
lastError = error;
|
|
259
|
+
if (!(error instanceof StoryTimeoutError) || attempts >= maxAttempts) {
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
console.warn(
|
|
263
|
+
`[screenshot] Retrying ${story.id} after timeout (attempt ${attempts + 1}/${maxAttempts})`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
completed++;
|
|
268
|
+
const absoluteIndex = offset + completed;
|
|
269
|
+
const durationMs = Date.now() - startedAt;
|
|
208
270
|
if (lightResult && darkResult) {
|
|
209
271
|
const fileHash = computeFileHash(story.importPath);
|
|
210
272
|
updateManifest(story.id, story.importPath, fileHash, lightResult.boundingBox);
|
|
273
|
+
console.log(
|
|
274
|
+
`[${absoluteIndex}/${displayTotal}] Generated screenshots for ${story.id}`,
|
|
275
|
+
{ storyId: story.id, attempts, durationMs, outcome: "ok" }
|
|
276
|
+
);
|
|
277
|
+
} else if (lastError) {
|
|
278
|
+
console.error(
|
|
279
|
+
`[${absoluteIndex}/${displayTotal}] \u26A0\uFE0F Failed ${story.id}:`,
|
|
280
|
+
lastError instanceof Error ? lastError.message : lastError,
|
|
281
|
+
{
|
|
282
|
+
storyId: story.id,
|
|
283
|
+
attempts,
|
|
284
|
+
durationMs,
|
|
285
|
+
outcome: lastError instanceof StoryTimeoutError ? "timeout" : "error"
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
} else {
|
|
289
|
+
console.warn(`[${absoluteIndex}/${displayTotal}] Partial result ${story.id}`, {
|
|
290
|
+
storyId: story.id,
|
|
291
|
+
attempts,
|
|
292
|
+
durationMs,
|
|
293
|
+
outcome: "partial",
|
|
294
|
+
hasLight: !!lightResult,
|
|
295
|
+
hasDark: !!darkResult
|
|
296
|
+
});
|
|
211
297
|
}
|
|
212
|
-
completed++;
|
|
213
|
-
const absoluteIndex = offset + completed;
|
|
214
|
-
console.log(
|
|
215
|
-
`[${absoluteIndex}/${displayTotal}] Generated screenshots for ${story.id}`
|
|
216
|
-
);
|
|
217
298
|
})
|
|
218
299
|
);
|
|
219
300
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
1
2
|
import { chromium } from 'playwright';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
globalThis.require = createRequire(import.meta.url);
|
|
4
5
|
var browser = null;
|
|
5
6
|
async function getBrowser() {
|
|
6
7
|
if (!browser) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlook/storybook-plugin",
|
|
3
|
-
"version": "0.3.1",
|
|
3
|
+
"version": "0.3.2-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"onlook-storybook": "./dist/cli/index.js"
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"typecheck": "tsc --noEmit",
|
|
30
30
|
"prepublishOnly": "bun run build && bun scripts/prepublish.ts",
|
|
31
31
|
"postpublish": "bun scripts/postpublish.ts",
|
|
32
|
-
"publish-pkg": "
|
|
32
|
+
"publish-pkg": "bun publish",
|
|
33
|
+
"publish-beta": "bun publish --tag beta"
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"@babel/generator": "^7.26.9",
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"playwright": "^1.52.0"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"@onbook/tsconfig": "
|
|
44
|
+
"@onbook/tsconfig": "0.1.0",
|
|
44
45
|
"@types/babel__generator": "^7.6.8",
|
|
45
46
|
"@types/babel__traverse": "^7.20.6",
|
|
46
47
|
"@types/node": "^22.15.32",
|