@onlook/storybook-plugin 0.3.6 → 0.3.7-beta.0

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 CHANGED
@@ -76,27 +76,6 @@ Screenshots are saved to `.storybook-cache/screenshots/`.
76
76
 
77
77
  The plugin auto-disables when `CHROMATIC` or `CI` environment variables are set.
78
78
 
79
- ## Publishing
80
-
81
- Three release paths:
82
-
83
- ```bash
84
- # 1. Ship the current version to npm as `latest` (default dist-tag).
85
- bun run publish-pkg
86
-
87
- # 2. Ship under the `next` dist-tag — for testing against preview deploys
88
- # without affecting consumers pinned to ^x.y.z ranges. Consumers opt in
89
- # via `@onlook/storybook-plugin@next`.
90
- bun run publish-pkg:next
91
-
92
- # 3. Promote a previously-published version to `latest`. Reads the version
93
- # from package.json, so bump first if you want to promote something else.
94
- bun run promote-latest
95
- ```
96
-
97
- The typical flow for risky changes: `publish-pkg:next` → test against a
98
- Vercel preview deploy → `promote-latest` once confident.
99
-
100
79
  ## License
101
80
 
102
81
  MIT
package/dist/cli/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
2
3
  import { command, string, boolean, run } from '@drizzle-team/brocli';
3
4
  import { spawn } from 'child_process';
4
5
  import fs, { readFileSync, existsSync } from 'fs';
@@ -6,6 +7,7 @@ import path, { resolve, join, dirname } from 'path';
6
7
  import crypto from 'crypto';
7
8
  import { chromium } from 'playwright';
8
9
 
10
+ globalThis.require = createRequire(import.meta.url);
9
11
  var CACHE_DIR = path.join(process.cwd(), ".storybook-cache");
10
12
  var SCREENSHOTS_DIR = path.join(CACHE_DIR, "screenshots");
11
13
  var MANIFEST_PATH = path.join(CACHE_DIR, "manifest.json");
@@ -190,6 +192,33 @@ async function generateScreenshot(storyId, theme, storybookUrl = "http://localho
190
192
  }
191
193
 
192
194
  // src/screenshot-service/screenshot-service.ts
195
+ var StoryTimeoutError = class extends Error {
196
+ constructor(storyId, timeoutMs) {
197
+ super(`Story ${storyId} timed out after ${timeoutMs / 1e3}s`);
198
+ this.storyId = storyId;
199
+ this.timeoutMs = timeoutMs;
200
+ this.name = "StoryTimeoutError";
201
+ }
202
+ };
203
+ async function captureBothThemes(storyId, storybookUrl, timeoutMs, storyTimeoutMs) {
204
+ let timer;
205
+ try {
206
+ return await Promise.race([
207
+ Promise.all([
208
+ generateScreenshot(storyId, "light", storybookUrl, timeoutMs),
209
+ generateScreenshot(storyId, "dark", storybookUrl, timeoutMs)
210
+ ]),
211
+ new Promise((_, reject) => {
212
+ timer = setTimeout(
213
+ () => reject(new StoryTimeoutError(storyId, storyTimeoutMs)),
214
+ storyTimeoutMs
215
+ );
216
+ })
217
+ ]);
218
+ } finally {
219
+ if (timer) clearTimeout(timer);
220
+ }
221
+ }
193
222
  async function generateAllScreenshots(stories, storybookUrl = "http://localhost:6006", concurrency = 10, offset = 0, total, skipExisting = false, timeoutMs = 3e4) {
194
223
  const displayTotal = total ?? offset + stories.length;
195
224
  console.log(
@@ -200,53 +229,74 @@ async function generateAllScreenshots(stories, storybookUrl = "http://localhost:
200
229
  for (let i = 0; i < stories.length; i += BATCH_SIZE) {
201
230
  batches.push(stories.slice(i, i + BATCH_SIZE));
202
231
  }
232
+ const storyTimeout = timeoutMs * 2 + 1e4;
203
233
  let completed = 0;
204
234
  for (const batch of batches) {
205
235
  await Promise.all(
206
236
  batch.map(async (story) => {
207
237
  if (skipExisting && screenshotExists(story.id, "light") && screenshotExists(story.id, "dark")) {
208
238
  completed++;
209
- const absoluteIndex = offset + completed;
210
- console.log(`[${absoluteIndex}/${displayTotal}] Skipped (exists) ${story.id}`);
239
+ const absoluteIndex2 = offset + completed;
240
+ console.log(`[${absoluteIndex2}/${displayTotal}] Skipped (exists) ${story.id}`);
211
241
  return;
212
242
  }
213
- const storyTimeout = timeoutMs * 2 + 1e4;
214
- let timer;
215
- try {
216
- const result = await Promise.race([
217
- Promise.all([
218
- generateScreenshot(story.id, "light", storybookUrl, timeoutMs),
219
- generateScreenshot(story.id, "dark", storybookUrl, timeoutMs)
220
- ]),
221
- new Promise((_, reject) => {
222
- timer = setTimeout(
223
- () => reject(
224
- new Error(
225
- `Story ${story.id} timed out after ${storyTimeout / 1e3}s`
226
- )
227
- ),
228
- storyTimeout
229
- );
230
- })
231
- ]);
232
- clearTimeout(timer);
233
- const [lightResult, darkResult] = result;
234
- if (lightResult && darkResult) {
235
- const fileHash = computeFileHash(story.importPath);
236
- updateManifest(story.id, story.importPath, fileHash, lightResult.boundingBox);
243
+ let lightResult = null;
244
+ let darkResult = null;
245
+ let lastError;
246
+ const startedAt = Date.now();
247
+ let attempts = 0;
248
+ const maxAttempts = 2;
249
+ while (attempts < maxAttempts) {
250
+ attempts++;
251
+ try {
252
+ [lightResult, darkResult] = await captureBothThemes(
253
+ story.id,
254
+ storybookUrl,
255
+ timeoutMs,
256
+ storyTimeout
257
+ );
258
+ lastError = void 0;
259
+ break;
260
+ } catch (error) {
261
+ lastError = error;
262
+ if (!(error instanceof StoryTimeoutError) || attempts >= maxAttempts) {
263
+ break;
264
+ }
265
+ console.warn(
266
+ `[screenshot] Retrying ${story.id} after timeout (attempt ${attempts + 1}/${maxAttempts})`
267
+ );
237
268
  }
238
- completed++;
239
- const absoluteIndex = offset + completed;
269
+ }
270
+ completed++;
271
+ const absoluteIndex = offset + completed;
272
+ const durationMs = Date.now() - startedAt;
273
+ if (lightResult && darkResult) {
274
+ const fileHash = computeFileHash(story.importPath);
275
+ updateManifest(story.id, story.importPath, fileHash, lightResult.boundingBox);
240
276
  console.log(
241
- `[${absoluteIndex}/${displayTotal}] Generated screenshots for ${story.id}`
277
+ `[${absoluteIndex}/${displayTotal}] Generated screenshots for ${story.id}`,
278
+ { storyId: story.id, attempts, durationMs, outcome: "ok" }
242
279
  );
243
- } catch (error) {
244
- completed++;
245
- const absoluteIndex = offset + completed;
280
+ } else if (lastError) {
246
281
  console.error(
247
282
  `[${absoluteIndex}/${displayTotal}] \u26A0\uFE0F Failed ${story.id}:`,
248
- error instanceof Error ? error.message : error
283
+ lastError instanceof Error ? lastError.message : lastError,
284
+ {
285
+ storyId: story.id,
286
+ attempts,
287
+ durationMs,
288
+ outcome: lastError instanceof StoryTimeoutError ? "timeout" : "error"
289
+ }
249
290
  );
291
+ } else {
292
+ console.warn(`[${absoluteIndex}/${displayTotal}] Partial result ${story.id}`, {
293
+ storyId: story.id,
294
+ attempts,
295
+ durationMs,
296
+ outcome: "partial",
297
+ hasLight: !!lightResult,
298
+ hasDark: !!darkResult
299
+ });
250
300
  }
251
301
  })
252
302
  );
package/dist/index.d.ts CHANGED
@@ -6,6 +6,10 @@ type OnlookPluginOptions = {
6
6
  port?: number;
7
7
  /** Additional allowed origins for CORS (merged with defaults) */
8
8
  allowedOrigins?: string[];
9
+ hmr?: {
10
+ protocol?: 'ws' | 'wss';
11
+ clientPort?: number;
12
+ };
9
13
  };
10
14
  declare function storybookOnlookPlugin(options?: OnlookPluginOptions): PluginOption[];
11
15