storybook-onbook-plugin 0.2.1 → 0.2.3
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.d.ts +1 -0
- package/dist/cli/index.js +395 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +453 -1
- package/dist/screenshot-service/index.d.ts +46 -4
- package/dist/screenshot-service/index.js +213 -5
- package/dist/screenshot-service/utils/browser/index.d.ts +12 -2
- package/dist/screenshot-service/utils/browser/index.js +25 -1
- package/package.json +28 -9
- package/dist/cli/generate-screenshots.d.ts +0 -3
- package/dist/cli/generate-screenshots.d.ts.map +0 -1
- package/dist/cli/generate-screenshots.js +0 -169
- package/dist/component-loc/component-loc.d.ts +0 -7
- package/dist/component-loc/component-loc.d.ts.map +0 -1
- package/dist/component-loc/component-loc.js +0 -58
- package/dist/component-loc/index.d.ts +0 -2
- package/dist/component-loc/index.d.ts.map +0 -1
- package/dist/component-loc/index.js +0 -1
- package/dist/handlers/handleStoryFileChange/handleStoryFileChange.d.ts +0 -3
- package/dist/handlers/handleStoryFileChange/handleStoryFileChange.d.ts.map +0 -1
- package/dist/handlers/handleStoryFileChange/handleStoryFileChange.js +0 -100
- package/dist/handlers/handleStoryFileChange/index.d.ts +0 -2
- package/dist/handlers/handleStoryFileChange/index.d.ts.map +0 -1
- package/dist/handlers/handleStoryFileChange/index.js +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/screenshot-service/constants.d.ts +0 -8
- package/dist/screenshot-service/constants.d.ts.map +0 -1
- package/dist/screenshot-service/constants.js +0 -11
- package/dist/screenshot-service/index.d.ts.map +0 -1
- package/dist/screenshot-service/screenshot-service.d.ts +0 -8
- package/dist/screenshot-service/screenshot-service.d.ts.map +0 -1
- package/dist/screenshot-service/screenshot-service.js +0 -36
- package/dist/screenshot-service/types.d.ts +0 -18
- package/dist/screenshot-service/types.d.ts.map +0 -1
- package/dist/screenshot-service/types.js +0 -1
- package/dist/screenshot-service/utils/browser/browser.d.ts +0 -10
- package/dist/screenshot-service/utils/browser/browser.d.ts.map +0 -1
- package/dist/screenshot-service/utils/browser/browser.js +0 -29
- package/dist/screenshot-service/utils/browser/index.d.ts.map +0 -1
- package/dist/screenshot-service/utils/screenshot/index.d.ts +0 -2
- package/dist/screenshot-service/utils/screenshot/index.d.ts.map +0 -1
- package/dist/screenshot-service/utils/screenshot/index.js +0 -1
- package/dist/screenshot-service/utils/screenshot/screenshot.d.ts +0 -26
- package/dist/screenshot-service/utils/screenshot/screenshot.d.ts.map +0 -1
- package/dist/screenshot-service/utils/screenshot/screenshot.js +0 -128
- package/dist/storybook-onlook-plugin.d.ts +0 -9
- package/dist/storybook-onlook-plugin.d.ts.map +0 -1
- package/dist/storybook-onlook-plugin.js +0 -148
- package/dist/utils/fileSystem/fileSystem.d.ts +0 -9
- package/dist/utils/fileSystem/fileSystem.d.ts.map +0 -1
- package/dist/utils/fileSystem/fileSystem.js +0 -24
- package/dist/utils/fileSystem/index.d.ts +0 -2
- package/dist/utils/fileSystem/index.d.ts.map +0 -1
- package/dist/utils/fileSystem/index.js +0 -1
- package/dist/utils/findGitRoot/findGitRoot.d.ts +0 -5
- package/dist/utils/findGitRoot/findGitRoot.d.ts.map +0 -1
- package/dist/utils/findGitRoot/findGitRoot.js +0 -15
- package/dist/utils/findGitRoot/index.d.ts +0 -2
- package/dist/utils/findGitRoot/index.d.ts.map +0 -1
- package/dist/utils/findGitRoot/index.js +0 -1
- package/dist/utils/manifest/index.d.ts +0 -2
- package/dist/utils/manifest/index.d.ts.map +0 -1
- package/dist/utils/manifest/index.js +0 -1
- package/dist/utils/manifest/manifest.d.ts +0 -21
- package/dist/utils/manifest/manifest.d.ts.map +0 -1
- package/dist/utils/manifest/manifest.js +0 -44
|
@@ -1,5 +1,213 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { chromium } from 'playwright';
|
|
5
|
+
|
|
6
|
+
// src/utils/fileSystem/fileSystem.ts
|
|
7
|
+
var CACHE_DIR = path.join(process.cwd(), ".storybook-cache");
|
|
8
|
+
var SCREENSHOTS_DIR = path.join(CACHE_DIR, "screenshots");
|
|
9
|
+
var MANIFEST_PATH = path.join(CACHE_DIR, "manifest.json");
|
|
10
|
+
var VIEWPORT_WIDTH = 1920;
|
|
11
|
+
var VIEWPORT_HEIGHT = 1080;
|
|
12
|
+
var MIN_COMPONENT_WIDTH = 420;
|
|
13
|
+
var MIN_COMPONENT_HEIGHT = 280;
|
|
14
|
+
|
|
15
|
+
// src/utils/fileSystem/fileSystem.ts
|
|
16
|
+
function ensureCacheDirectories() {
|
|
17
|
+
if (!fs.existsSync(CACHE_DIR)) {
|
|
18
|
+
fs.mkdirSync(CACHE_DIR, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
if (!fs.existsSync(SCREENSHOTS_DIR)) {
|
|
21
|
+
fs.mkdirSync(SCREENSHOTS_DIR, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function computeFileHash(filePath) {
|
|
25
|
+
if (!fs.existsSync(filePath)) {
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
29
|
+
return crypto.createHash("sha256").update(content).digest("hex");
|
|
30
|
+
}
|
|
31
|
+
function loadManifest() {
|
|
32
|
+
if (fs.existsSync(MANIFEST_PATH)) {
|
|
33
|
+
const content = fs.readFileSync(MANIFEST_PATH, "utf-8");
|
|
34
|
+
return JSON.parse(content);
|
|
35
|
+
}
|
|
36
|
+
return { stories: {} };
|
|
37
|
+
}
|
|
38
|
+
function saveManifest(manifest) {
|
|
39
|
+
ensureCacheDirectories();
|
|
40
|
+
fs.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
41
|
+
}
|
|
42
|
+
function updateManifest(storyId, sourcePath, fileHash, boundingBox) {
|
|
43
|
+
const manifest = loadManifest();
|
|
44
|
+
manifest.stories[storyId] = {
|
|
45
|
+
fileHash,
|
|
46
|
+
lastGenerated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
47
|
+
sourcePath,
|
|
48
|
+
screenshots: {
|
|
49
|
+
light: `screenshots/${storyId}/light.png`,
|
|
50
|
+
dark: `screenshots/${storyId}/dark.png`
|
|
51
|
+
},
|
|
52
|
+
boundingBox: boundingBox ?? void 0
|
|
53
|
+
};
|
|
54
|
+
saveManifest(manifest);
|
|
55
|
+
}
|
|
56
|
+
var browser = null;
|
|
57
|
+
async function getBrowser() {
|
|
58
|
+
if (!browser) {
|
|
59
|
+
browser = await chromium.launch({
|
|
60
|
+
headless: true
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return browser;
|
|
64
|
+
}
|
|
65
|
+
async function closeBrowser() {
|
|
66
|
+
if (browser) {
|
|
67
|
+
try {
|
|
68
|
+
await browser.close();
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error("Error closing browser:", error);
|
|
71
|
+
} finally {
|
|
72
|
+
browser = null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function getScreenshotPath(storyId, theme) {
|
|
77
|
+
const storyDir = path.join(SCREENSHOTS_DIR, storyId);
|
|
78
|
+
return path.join(storyDir, `${theme}.png`);
|
|
79
|
+
}
|
|
80
|
+
function screenshotExists(storyId, theme) {
|
|
81
|
+
const screenshotPath = getScreenshotPath(storyId, theme);
|
|
82
|
+
return fs.existsSync(screenshotPath);
|
|
83
|
+
}
|
|
84
|
+
async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, height = VIEWPORT_HEIGHT, storybookUrl = "http://localhost:6006") {
|
|
85
|
+
const browser2 = await getBrowser();
|
|
86
|
+
const context = await browser2.newContext({
|
|
87
|
+
viewport: { width, height },
|
|
88
|
+
deviceScaleFactor: 2
|
|
89
|
+
});
|
|
90
|
+
const page = await context.newPage();
|
|
91
|
+
try {
|
|
92
|
+
const url = `${storybookUrl}/iframe.html?id=${storyId}&viewMode=story&globals=theme:${theme}`;
|
|
93
|
+
await page.goto(url, { timeout: 15e3 });
|
|
94
|
+
await page.waitForLoadState("domcontentloaded");
|
|
95
|
+
await page.waitForLoadState("load");
|
|
96
|
+
await page.waitForLoadState("networkidle");
|
|
97
|
+
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
|
+
});
|
|
110
|
+
const contentBounds = await page.evaluate(() => {
|
|
111
|
+
const root = document.querySelector("#storybook-root");
|
|
112
|
+
if (!root) return null;
|
|
113
|
+
const children = root.querySelectorAll("*");
|
|
114
|
+
if (children.length === 0) return null;
|
|
115
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
116
|
+
children.forEach((child) => {
|
|
117
|
+
const rect = child.getBoundingClientRect();
|
|
118
|
+
if (rect.width === 0 || rect.height === 0) return;
|
|
119
|
+
minX = Math.min(minX, rect.left);
|
|
120
|
+
minY = Math.min(minY, rect.top);
|
|
121
|
+
maxX = Math.max(maxX, rect.right);
|
|
122
|
+
maxY = Math.max(maxY, rect.bottom);
|
|
123
|
+
});
|
|
124
|
+
if (minX === Infinity) return null;
|
|
125
|
+
return {
|
|
126
|
+
x: minX,
|
|
127
|
+
y: minY,
|
|
128
|
+
width: maxX - minX,
|
|
129
|
+
height: maxY - minY
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
let screenshotBuffer;
|
|
133
|
+
let resultBoundingBox = null;
|
|
134
|
+
if (contentBounds && contentBounds.width > 0 && contentBounds.height > 0) {
|
|
135
|
+
const PADDING = 20;
|
|
136
|
+
const clippedWidth = Math.min(width, contentBounds.width + PADDING);
|
|
137
|
+
const clippedHeight = Math.min(height, contentBounds.height + PADDING);
|
|
138
|
+
resultBoundingBox = {
|
|
139
|
+
width: Math.max(MIN_COMPONENT_WIDTH, Math.round(clippedWidth)),
|
|
140
|
+
height: Math.max(MIN_COMPONENT_HEIGHT, Math.round(clippedHeight))
|
|
141
|
+
};
|
|
142
|
+
screenshotBuffer = await page.screenshot({
|
|
143
|
+
type: "png",
|
|
144
|
+
clip: {
|
|
145
|
+
x: Math.max(0, contentBounds.x - 10),
|
|
146
|
+
y: Math.max(0, contentBounds.y - 10),
|
|
147
|
+
width: clippedWidth,
|
|
148
|
+
height: clippedHeight
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
screenshotBuffer = await page.screenshot({ type: "png" });
|
|
153
|
+
}
|
|
154
|
+
return { buffer: screenshotBuffer, boundingBox: resultBoundingBox };
|
|
155
|
+
} finally {
|
|
156
|
+
await context.close();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async function generateScreenshot(storyId, theme, storybookUrl = "http://localhost:6006") {
|
|
160
|
+
try {
|
|
161
|
+
ensureCacheDirectories();
|
|
162
|
+
const storyDir = path.join(SCREENSHOTS_DIR, storyId);
|
|
163
|
+
if (!fs.existsSync(storyDir)) {
|
|
164
|
+
fs.mkdirSync(storyDir, { recursive: true });
|
|
165
|
+
}
|
|
166
|
+
const screenshotPath = getScreenshotPath(storyId, theme);
|
|
167
|
+
const { buffer, boundingBox } = await captureScreenshotBuffer(
|
|
168
|
+
storyId,
|
|
169
|
+
theme,
|
|
170
|
+
VIEWPORT_WIDTH,
|
|
171
|
+
VIEWPORT_HEIGHT,
|
|
172
|
+
storybookUrl
|
|
173
|
+
);
|
|
174
|
+
fs.writeFileSync(screenshotPath, buffer);
|
|
175
|
+
return { path: screenshotPath, boundingBox };
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error(`Error generating screenshot for ${storyId} (${theme}):`, error);
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/screenshot-service/screenshot-service.ts
|
|
183
|
+
async function generateAllScreenshots(stories, storybookUrl = "http://localhost:6006") {
|
|
184
|
+
console.log(`Generating screenshots for ${stories.length} stories...`);
|
|
185
|
+
const BATCH_SIZE = 10;
|
|
186
|
+
const batches = [];
|
|
187
|
+
for (let i = 0; i < stories.length; i += BATCH_SIZE) {
|
|
188
|
+
batches.push(stories.slice(i, i + BATCH_SIZE));
|
|
189
|
+
}
|
|
190
|
+
let completed = 0;
|
|
191
|
+
for (const batch of batches) {
|
|
192
|
+
await Promise.all(
|
|
193
|
+
batch.map(async (story) => {
|
|
194
|
+
const [lightResult, darkResult] = await Promise.all([
|
|
195
|
+
generateScreenshot(story.id, "light", storybookUrl),
|
|
196
|
+
generateScreenshot(story.id, "dark", storybookUrl)
|
|
197
|
+
]);
|
|
198
|
+
if (lightResult && darkResult) {
|
|
199
|
+
const fileHash = computeFileHash(story.importPath);
|
|
200
|
+
updateManifest(story.id, story.importPath, fileHash, lightResult.boundingBox);
|
|
201
|
+
}
|
|
202
|
+
completed++;
|
|
203
|
+
console.log(
|
|
204
|
+
`[${completed}/${stories.length}] Generated screenshots for ${story.id}`
|
|
205
|
+
);
|
|
206
|
+
})
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
await closeBrowser();
|
|
210
|
+
console.log("Screenshot generation complete!");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export { closeBrowser, generateAllScreenshots, generateScreenshot, getScreenshotPath, screenshotExists };
|
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { Browser } from 'playwright';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Initialize browser instance
|
|
5
|
+
*/
|
|
6
|
+
declare function getBrowser(): Promise<Browser>;
|
|
7
|
+
/**
|
|
8
|
+
* Close browser instance
|
|
9
|
+
*/
|
|
10
|
+
declare function closeBrowser(): Promise<void>;
|
|
11
|
+
|
|
12
|
+
export { closeBrowser, getBrowser };
|
|
@@ -1 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
import { chromium } from 'playwright';
|
|
2
|
+
|
|
3
|
+
// src/screenshot-service/utils/browser/browser.ts
|
|
4
|
+
var browser = null;
|
|
5
|
+
async function getBrowser() {
|
|
6
|
+
if (!browser) {
|
|
7
|
+
browser = await chromium.launch({
|
|
8
|
+
headless: true
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
return browser;
|
|
12
|
+
}
|
|
13
|
+
async function closeBrowser() {
|
|
14
|
+
if (browser) {
|
|
15
|
+
try {
|
|
16
|
+
await browser.close();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error("Error closing browser:", error);
|
|
19
|
+
} finally {
|
|
20
|
+
browser = null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { closeBrowser, getBrowser };
|
package/package.json
CHANGED
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "storybook-onbook-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
|
-
"
|
|
6
|
+
"storybook-onbook-plugin": "./dist/cli/index.js"
|
|
7
7
|
},
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"types": "./
|
|
11
|
-
"
|
|
10
|
+
"types": "./src/index.ts",
|
|
11
|
+
"default": "./src/index.ts"
|
|
12
12
|
},
|
|
13
13
|
"./screenshot-service": {
|
|
14
|
-
"types": "./
|
|
15
|
-
"
|
|
14
|
+
"types": "./src/screenshot-service/index.ts",
|
|
15
|
+
"default": "./src/screenshot-service/index.ts"
|
|
16
16
|
},
|
|
17
17
|
"./screenshot-service/browser": {
|
|
18
|
-
"types": "./
|
|
19
|
-
"
|
|
18
|
+
"types": "./src/screenshot-service/utils/browser/index.ts",
|
|
19
|
+
"default": "./src/screenshot-service/utils/browser/index.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./screenshot-service": {
|
|
29
|
+
"types": "./dist/screenshot-service/index.d.ts",
|
|
30
|
+
"import": "./dist/screenshot-service/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./screenshot-service/browser": {
|
|
33
|
+
"types": "./dist/screenshot-service/utils/browser/index.d.ts",
|
|
34
|
+
"import": "./dist/screenshot-service/utils/browser/index.js"
|
|
35
|
+
}
|
|
20
36
|
}
|
|
21
37
|
},
|
|
22
38
|
"files": [
|
|
@@ -24,7 +40,7 @@
|
|
|
24
40
|
"README.md"
|
|
25
41
|
],
|
|
26
42
|
"scripts": {
|
|
27
|
-
"build": "
|
|
43
|
+
"build": "tsup",
|
|
28
44
|
"clean": "git clean -xdf .cache .turbo dist node_modules",
|
|
29
45
|
"typecheck": "tsc --noEmit",
|
|
30
46
|
"prepublishOnly": "bun run build",
|
|
@@ -35,6 +51,7 @@
|
|
|
35
51
|
"@babel/parser": "^7.26.9",
|
|
36
52
|
"@babel/traverse": "^7.26.9",
|
|
37
53
|
"@babel/types": "^7.26.9",
|
|
54
|
+
"@drizzle-team/brocli": "^0.11.0",
|
|
38
55
|
"playwright": "^1.52.0"
|
|
39
56
|
},
|
|
40
57
|
"devDependencies": {
|
|
@@ -42,6 +59,8 @@
|
|
|
42
59
|
"@types/babel__generator": "^7.6.8",
|
|
43
60
|
"@types/babel__traverse": "^7.20.6",
|
|
44
61
|
"@types/node": "^22.15.32",
|
|
62
|
+
"bun-types": "^1.3.5",
|
|
63
|
+
"tsup": "^8.5.1",
|
|
45
64
|
"typescript": "5.8.3",
|
|
46
65
|
"vite": "^6.3.5"
|
|
47
66
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generate-screenshots.d.ts","sourceRoot":"","sources":["../../src/cli/generate-screenshots.ts"],"names":[],"mappings":""}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
3
|
-
import { generateAllScreenshots } from '../screenshot-service/index.js';
|
|
4
|
-
import { getBrowser } from '../screenshot-service/utils/browser/index.js';
|
|
5
|
-
function parseArgs() {
|
|
6
|
-
const args = process.argv.slice(2);
|
|
7
|
-
const options = {
|
|
8
|
-
url: 'http://localhost:6006',
|
|
9
|
-
startStorybook: false,
|
|
10
|
-
storybookCmd: 'npm run storybook',
|
|
11
|
-
};
|
|
12
|
-
for (let i = 0; i < args.length; i++) {
|
|
13
|
-
const arg = args[i];
|
|
14
|
-
const nextArg = args[i + 1];
|
|
15
|
-
if (arg === '--url' && nextArg) {
|
|
16
|
-
options.url = nextArg;
|
|
17
|
-
i++;
|
|
18
|
-
}
|
|
19
|
-
else if (arg === '--start') {
|
|
20
|
-
options.startStorybook = true;
|
|
21
|
-
}
|
|
22
|
-
else if (arg === '--cmd' && nextArg) {
|
|
23
|
-
options.storybookCmd = nextArg;
|
|
24
|
-
i++;
|
|
25
|
-
}
|
|
26
|
-
else if (arg === '--help' || arg === '-h') {
|
|
27
|
-
console.log(`
|
|
28
|
-
Usage: generate-screenshots [options]
|
|
29
|
-
|
|
30
|
-
Options:
|
|
31
|
-
--url <url> Storybook URL (default: http://localhost:6006)
|
|
32
|
-
--start Start Storybook before generating screenshots
|
|
33
|
-
--cmd <command> Command to start Storybook (default: npm run storybook)
|
|
34
|
-
--help, -h Show this help message
|
|
35
|
-
|
|
36
|
-
Examples:
|
|
37
|
-
# With Storybook already running
|
|
38
|
-
generate-screenshots
|
|
39
|
-
|
|
40
|
-
# Start Storybook automatically
|
|
41
|
-
generate-screenshots --start
|
|
42
|
-
|
|
43
|
-
# Custom URL
|
|
44
|
-
generate-screenshots --url http://localhost:9009
|
|
45
|
-
|
|
46
|
-
# Custom start command
|
|
47
|
-
generate-screenshots --start --cmd "bun run storybook"
|
|
48
|
-
`);
|
|
49
|
-
process.exit(0);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return options;
|
|
53
|
-
}
|
|
54
|
-
async function fetchStoryIndex(url) {
|
|
55
|
-
const indexUrl = `${url}/index.json`;
|
|
56
|
-
try {
|
|
57
|
-
const response = await fetch(indexUrl);
|
|
58
|
-
if (!response.ok) {
|
|
59
|
-
throw new Error(`Failed to fetch story index: ${response.statusText}`);
|
|
60
|
-
}
|
|
61
|
-
const data = (await response.json());
|
|
62
|
-
return Object.values(data.entries);
|
|
63
|
-
}
|
|
64
|
-
catch (error) {
|
|
65
|
-
console.error('Error fetching story index:', error);
|
|
66
|
-
console.error(`Make sure Storybook is running at ${url}`);
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
async function startStorybook(cmd) {
|
|
71
|
-
console.log(`🚀 Starting Storybook with: ${cmd}`);
|
|
72
|
-
const [command, ...args] = cmd.split(' ');
|
|
73
|
-
if (!command) {
|
|
74
|
-
throw new Error('Invalid storybook command');
|
|
75
|
-
}
|
|
76
|
-
const storybookProcess = spawn(command, [...args, '--', '--no-open'], {
|
|
77
|
-
cwd: process.cwd(),
|
|
78
|
-
stdio: 'pipe',
|
|
79
|
-
shell: true,
|
|
80
|
-
});
|
|
81
|
-
return new Promise((resolve, reject) => {
|
|
82
|
-
let started = false;
|
|
83
|
-
const timeout = setTimeout(() => {
|
|
84
|
-
if (!started) {
|
|
85
|
-
storybookProcess.kill();
|
|
86
|
-
reject(new Error('Storybook failed to start within 60 seconds'));
|
|
87
|
-
}
|
|
88
|
-
}, 60000);
|
|
89
|
-
storybookProcess.stdout?.on('data', (data) => {
|
|
90
|
-
const output = data.toString();
|
|
91
|
-
console.log(output);
|
|
92
|
-
if (output.includes('Local:') || output.includes('localhost:')) {
|
|
93
|
-
if (!started) {
|
|
94
|
-
started = true;
|
|
95
|
-
clearTimeout(timeout);
|
|
96
|
-
console.log('✅ Storybook is ready!');
|
|
97
|
-
resolve(storybookProcess);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
storybookProcess.stderr?.on('data', (data) => {
|
|
102
|
-
console.error(data.toString());
|
|
103
|
-
});
|
|
104
|
-
storybookProcess.on('error', (error) => {
|
|
105
|
-
clearTimeout(timeout);
|
|
106
|
-
reject(error);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
async function warmupStorybook(url, firstStoryId) {
|
|
111
|
-
console.log('🔥 Warming up Storybook...');
|
|
112
|
-
const browser = await getBrowser();
|
|
113
|
-
const context = await browser.newContext();
|
|
114
|
-
const page = await context.newPage();
|
|
115
|
-
try {
|
|
116
|
-
const warmupUrl = `${url}/iframe.html?id=${firstStoryId}&viewMode=story`;
|
|
117
|
-
await page.goto(warmupUrl, { timeout: 15000 });
|
|
118
|
-
await page.waitForLoadState('networkidle');
|
|
119
|
-
console.log('✅ Storybook warmed up');
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
console.log('⚠️ Warmup had issues, proceeding anyway:', error);
|
|
123
|
-
}
|
|
124
|
-
finally {
|
|
125
|
-
await context.close();
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
async function main() {
|
|
129
|
-
const options = parseArgs();
|
|
130
|
-
console.log('📸 Generating Storybook screenshots...');
|
|
131
|
-
let storybookProcess = null;
|
|
132
|
-
try {
|
|
133
|
-
if (options.startStorybook) {
|
|
134
|
-
storybookProcess = await startStorybook(options.storybookCmd);
|
|
135
|
-
}
|
|
136
|
-
console.log(`Using Storybook URL: ${options.url}`);
|
|
137
|
-
// Fetch all stories
|
|
138
|
-
const stories = await fetchStoryIndex(options.url);
|
|
139
|
-
console.log(`Found ${stories.length} stories`);
|
|
140
|
-
const firstStory = stories[0];
|
|
141
|
-
if (!firstStory) {
|
|
142
|
-
throw new Error('No stories found');
|
|
143
|
-
}
|
|
144
|
-
// Warm up Storybook
|
|
145
|
-
await warmupStorybook(options.url, firstStory.id);
|
|
146
|
-
// Generate screenshots
|
|
147
|
-
await generateAllScreenshots(stories.map((story) => ({
|
|
148
|
-
id: story.id,
|
|
149
|
-
importPath: story.importPath,
|
|
150
|
-
})), options.url);
|
|
151
|
-
console.log('✅ Screenshot generation complete!');
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
console.error('❌ Error during screenshot generation:', error);
|
|
155
|
-
throw error;
|
|
156
|
-
}
|
|
157
|
-
finally {
|
|
158
|
-
if (storybookProcess) {
|
|
159
|
-
console.log('🛑 Stopping Storybook...');
|
|
160
|
-
storybookProcess.kill();
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
main()
|
|
165
|
-
.then(() => process.exit(0))
|
|
166
|
-
.catch((error) => {
|
|
167
|
-
console.error('Fatal error:', error);
|
|
168
|
-
process.exit(1);
|
|
169
|
-
});
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { Plugin } from 'vite';
|
|
2
|
-
type ComponentLocPluginOptions = {
|
|
3
|
-
include?: RegExp;
|
|
4
|
-
};
|
|
5
|
-
export declare function componentLocPlugin(options?: ComponentLocPluginOptions): Plugin;
|
|
6
|
-
export default componentLocPlugin;
|
|
7
|
-
//# sourceMappingURL=component-loc.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"component-loc.d.ts","sourceRoot":"","sources":["../../src/component-loc/component-loc.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,KAAK,yBAAyB,GAAG;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG,MAAM,CAgFlF;AAED,eAAe,kBAAkB,CAAC"}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import generateModule from '@babel/generator';
|
|
3
|
-
import { parse } from '@babel/parser';
|
|
4
|
-
import traverseModule from '@babel/traverse';
|
|
5
|
-
import * as t from '@babel/types';
|
|
6
|
-
export function componentLocPlugin(options = {}) {
|
|
7
|
-
const include = options.include ?? /\.(jsx|tsx)$/;
|
|
8
|
-
// @babel/traverse and @babel/generator are CommonJS modules with default exports.
|
|
9
|
-
// When imported in an ESM context, the default export may be on `.default` or directly on the module.
|
|
10
|
-
// This workaround handles both cases to ensure compatibility across different bundler configurations.
|
|
11
|
-
const traverse = traverseModule.default ??
|
|
12
|
-
traverseModule;
|
|
13
|
-
const generate = generateModule.default ??
|
|
14
|
-
generateModule;
|
|
15
|
-
let root;
|
|
16
|
-
return {
|
|
17
|
-
name: 'onbook-component-loc',
|
|
18
|
-
enforce: 'pre',
|
|
19
|
-
apply: 'serve',
|
|
20
|
-
configResolved(config) {
|
|
21
|
-
root = config.root;
|
|
22
|
-
},
|
|
23
|
-
transform(code, id) {
|
|
24
|
-
const filepath = id.split('?', 1)[0];
|
|
25
|
-
if (!filepath || filepath.includes('node_modules'))
|
|
26
|
-
return null;
|
|
27
|
-
if (!include.test(filepath))
|
|
28
|
-
return null;
|
|
29
|
-
const ast = parse(code, {
|
|
30
|
-
sourceType: 'module',
|
|
31
|
-
plugins: ['jsx', 'typescript'],
|
|
32
|
-
sourceFilename: filepath,
|
|
33
|
-
});
|
|
34
|
-
let mutated = false;
|
|
35
|
-
// Get relative path from project root
|
|
36
|
-
const relativePath = path.relative(root, filepath);
|
|
37
|
-
traverse(ast, {
|
|
38
|
-
JSXElement(nodePath) {
|
|
39
|
-
const opening = nodePath.node.openingElement;
|
|
40
|
-
const element = nodePath.node;
|
|
41
|
-
if (!opening.loc || !element.loc)
|
|
42
|
-
return;
|
|
43
|
-
const alreadyTagged = opening.attributes.some((attribute) => t.isJSXAttribute(attribute) &&
|
|
44
|
-
attribute.name.name === 'data-component-file');
|
|
45
|
-
if (alreadyTagged)
|
|
46
|
-
return;
|
|
47
|
-
opening.attributes.push(t.jsxAttribute(t.jsxIdentifier('data-component-file'), t.stringLiteral(relativePath)), t.jsxAttribute(t.jsxIdentifier('data-component-start'), t.stringLiteral(`${element.loc.start.line}:${element.loc.start.column}`)), t.jsxAttribute(t.jsxIdentifier('data-component-end'), t.stringLiteral(`${element.loc.end.line}:${element.loc.end.column}`)));
|
|
48
|
-
mutated = true;
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
if (!mutated)
|
|
52
|
-
return null;
|
|
53
|
-
const output = generate(ast, { retainLines: true, sourceMaps: true, sourceFileName: id }, code);
|
|
54
|
-
return { code: output.code, map: output.map };
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
export default componentLocPlugin;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/component-loc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { componentLocPlugin } from './component-loc.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"handleStoryFileChange.d.ts","sourceRoot":"","sources":["../../../src/handlers/handleStoryFileChange/handleStoryFileChange.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AA4FvC,wBAAgB,qBAAqB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,UAAU,2CAiClE"}
|