@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.
@@ -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
- // src/utils/fileSystem/fileSystem.ts
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
- await page.waitForLoadState("networkidle", { timeout: timeoutMs });
97
+ try {
98
+ await page.waitForLoadState("networkidle", { timeout: 5e3 });
99
+ } catch {
100
+ }
97
101
  await page.evaluate(() => document.fonts.ready);
98
- await page.evaluate(async () => {
99
- const images = document.querySelectorAll("img");
100
- await Promise.all(
101
- Array.from(images).map((img) => {
102
- if (img.complete) return Promise.resolve();
103
- return new Promise((resolve) => {
104
- img.addEventListener("load", resolve);
105
- img.addEventListener("error", resolve);
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
- const [lightResult, darkResult] = await Promise.all([
205
- generateScreenshot(story.id, "light", storybookUrl, timeoutMs),
206
- generateScreenshot(story.id, "dark", storybookUrl, timeoutMs)
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
- // src/screenshot-service/utils/browser/browser.ts
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": "npm publish"
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": "workspace:*",
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",