@onlook/storybook-plugin 0.4.0-beta.1 → 0.4.0-beta.2
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/index.js +155 -41
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fs5, { existsSync } from 'fs';
|
|
2
|
-
import
|
|
2
|
+
import path6, { dirname, join, relative } from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
|
-
import autoStoryGenerator from '@takuma-ru/auto-story-generator';
|
|
5
4
|
import { withDefaultConfig } from 'react-docgen-typescript';
|
|
6
5
|
import generateModule from '@babel/generator';
|
|
7
6
|
import { parse } from '@babel/parser';
|
|
@@ -10,7 +9,12 @@ import * as t from '@babel/types';
|
|
|
10
9
|
import crypto from 'crypto';
|
|
11
10
|
import { chromium } from 'playwright';
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
13
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
14
|
+
}) : x)(function(x) {
|
|
15
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
16
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
17
|
+
});
|
|
14
18
|
var FIXED_MARKER = "// @onlook-fixed";
|
|
15
19
|
var parser = withDefaultConfig({
|
|
16
20
|
shouldExtractLiteralValuesFromEnum: true,
|
|
@@ -21,11 +25,11 @@ var parser = withDefaultConfig({
|
|
|
21
25
|
}
|
|
22
26
|
});
|
|
23
27
|
function resolveComponentPath(storyFilePath) {
|
|
24
|
-
const dir =
|
|
25
|
-
const parentDir =
|
|
26
|
-
const storyName =
|
|
28
|
+
const dir = path6.dirname(storyFilePath);
|
|
29
|
+
const parentDir = path6.dirname(dir);
|
|
30
|
+
const storyName = path6.basename(storyFilePath);
|
|
27
31
|
const componentName = storyName.replace(".stories.tsx", ".tsx");
|
|
28
|
-
const componentPath =
|
|
32
|
+
const componentPath = path6.join(parentDir, componentName);
|
|
29
33
|
return fs5.existsSync(componentPath) ? componentPath : null;
|
|
30
34
|
}
|
|
31
35
|
function generateArgTypes(componentPath) {
|
|
@@ -87,7 +91,7 @@ export default meta;`
|
|
|
87
91
|
);
|
|
88
92
|
if (enriched !== content) {
|
|
89
93
|
fs5.writeFileSync(storyFilePath, enriched);
|
|
90
|
-
console.log(`[AutoStories] Enriched ${
|
|
94
|
+
console.log(`[AutoStories] Enriched ${path6.basename(storyFilePath)} with argTypes`);
|
|
91
95
|
}
|
|
92
96
|
} catch (err) {
|
|
93
97
|
console.error(`[AutoStories] Failed to enrich story: ${storyFilePath}`, err);
|
|
@@ -115,7 +119,7 @@ function componentLocPlugin(options = {}) {
|
|
|
115
119
|
sourceFilename: filepath
|
|
116
120
|
});
|
|
117
121
|
let mutated = false;
|
|
118
|
-
const relativePath =
|
|
122
|
+
const relativePath = path6.relative(root, filepath);
|
|
119
123
|
traverse(ast, {
|
|
120
124
|
JSXElement(nodePath) {
|
|
121
125
|
const opening = nodePath.node.openingElement;
|
|
@@ -152,9 +156,9 @@ function componentLocPlugin(options = {}) {
|
|
|
152
156
|
}
|
|
153
157
|
};
|
|
154
158
|
}
|
|
155
|
-
var CACHE_DIR =
|
|
156
|
-
var SCREENSHOTS_DIR =
|
|
157
|
-
var MANIFEST_PATH =
|
|
159
|
+
var CACHE_DIR = path6.join(process.cwd(), ".storybook-cache");
|
|
160
|
+
var SCREENSHOTS_DIR = path6.join(CACHE_DIR, "screenshots");
|
|
161
|
+
var MANIFEST_PATH = path6.join(CACHE_DIR, "manifest.json");
|
|
158
162
|
var VIEWPORT_WIDTH = 1920;
|
|
159
163
|
var VIEWPORT_HEIGHT = 1080;
|
|
160
164
|
var MIN_COMPONENT_WIDTH = 420;
|
|
@@ -211,8 +215,8 @@ async function getBrowser() {
|
|
|
211
215
|
return browser;
|
|
212
216
|
}
|
|
213
217
|
function getScreenshotPath(storyId, theme) {
|
|
214
|
-
const storyDir =
|
|
215
|
-
return
|
|
218
|
+
const storyDir = path6.join(SCREENSHOTS_DIR, storyId);
|
|
219
|
+
return path6.join(storyDir, `${theme}.png`);
|
|
216
220
|
}
|
|
217
221
|
async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, height = VIEWPORT_HEIGHT, storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
218
222
|
const browser2 = await getBrowser();
|
|
@@ -299,7 +303,7 @@ async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, h
|
|
|
299
303
|
async function generateScreenshot(storyId, theme, storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
300
304
|
try {
|
|
301
305
|
ensureCacheDirectories();
|
|
302
|
-
const storyDir =
|
|
306
|
+
const storyDir = path6.join(SCREENSHOTS_DIR, storyId);
|
|
303
307
|
if (!fs5.existsSync(storyDir)) {
|
|
304
308
|
fs5.mkdirSync(storyDir, { recursive: true });
|
|
305
309
|
}
|
|
@@ -339,7 +343,7 @@ async function fetchStorybookIndex() {
|
|
|
339
343
|
}
|
|
340
344
|
function getStoriesForFile(filePath) {
|
|
341
345
|
if (!cachedIndex) return [];
|
|
342
|
-
const fileName =
|
|
346
|
+
const fileName = path6.basename(filePath);
|
|
343
347
|
return Object.values(cachedIndex.entries).filter((entry) => entry.type === "story" && entry.importPath.endsWith(fileName)).map((entry) => entry.id);
|
|
344
348
|
}
|
|
345
349
|
async function regenerateScreenshotsForFiles(files) {
|
|
@@ -428,6 +432,18 @@ var DEFAULT_ALLOWED_ORIGINS = [
|
|
|
428
432
|
];
|
|
429
433
|
var AUTO_STORIES_FOLDER = ".onlook-stories";
|
|
430
434
|
var storyRuntimeErrors = /* @__PURE__ */ new Map();
|
|
435
|
+
var discoveryConfig = {
|
|
436
|
+
imports: ["src/**/*.tsx"],
|
|
437
|
+
ignores: []
|
|
438
|
+
};
|
|
439
|
+
function deriveTitleFromPath(filePath) {
|
|
440
|
+
const parts = filePath.replace(/^src\//, "").split("/");
|
|
441
|
+
parts.pop();
|
|
442
|
+
return parts.filter((p) => !p.startsWith("(") && !p.startsWith("[")).join("/");
|
|
443
|
+
}
|
|
444
|
+
function toStoryId(title, name) {
|
|
445
|
+
return `${title.replace(/\//g, "-").toLowerCase()}--${name.toLowerCase()}`;
|
|
446
|
+
}
|
|
431
447
|
var serveMetadataAndScreenshots = (req, res, next) => {
|
|
432
448
|
if (req.url === "/onbook-health.json") {
|
|
433
449
|
const cacheBuster = Date.now();
|
|
@@ -499,9 +515,124 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
499
515
|
});
|
|
500
516
|
return;
|
|
501
517
|
}
|
|
518
|
+
if (req.url === "/onbook-components.json") {
|
|
519
|
+
try {
|
|
520
|
+
const { globSync } = __require("glob");
|
|
521
|
+
const files = discoveryConfig.imports.flatMap(
|
|
522
|
+
(pattern) => globSync(pattern, {
|
|
523
|
+
ignore: discoveryConfig.ignores,
|
|
524
|
+
cwd: process.cwd()
|
|
525
|
+
})
|
|
526
|
+
);
|
|
527
|
+
const components = files.map((file) => ({
|
|
528
|
+
filePath: file,
|
|
529
|
+
componentName: path6.basename(file, path6.extname(file)),
|
|
530
|
+
title: deriveTitleFromPath(file)
|
|
531
|
+
}));
|
|
532
|
+
res.setHeader("Content-Type", "application/json");
|
|
533
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
534
|
+
res.end(JSON.stringify({ components }));
|
|
535
|
+
} catch (error) {
|
|
536
|
+
res.statusCode = 500;
|
|
537
|
+
res.setHeader("Content-Type", "application/json");
|
|
538
|
+
res.end(
|
|
539
|
+
JSON.stringify({
|
|
540
|
+
error: "Failed to discover components",
|
|
541
|
+
details: String(error)
|
|
542
|
+
})
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (req.url === "/onbook-generate-story" && req.method === "POST") {
|
|
548
|
+
let body = "";
|
|
549
|
+
req.on("data", (chunk) => {
|
|
550
|
+
body += chunk.toString();
|
|
551
|
+
});
|
|
552
|
+
req.on("end", () => {
|
|
553
|
+
try {
|
|
554
|
+
const { filePath } = JSON.parse(body);
|
|
555
|
+
if (!filePath) {
|
|
556
|
+
res.statusCode = 400;
|
|
557
|
+
res.setHeader("Content-Type", "application/json");
|
|
558
|
+
res.end(JSON.stringify({ error: "filePath is required" }));
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
const absPath = path6.resolve(filePath);
|
|
562
|
+
const dir = path6.dirname(absPath);
|
|
563
|
+
const ext = path6.extname(absPath);
|
|
564
|
+
const baseName = path6.basename(absPath, ext);
|
|
565
|
+
const storyDir = path6.join(dir, AUTO_STORIES_FOLDER);
|
|
566
|
+
const storyPath = path6.join(storyDir, `${baseName}.stories.tsx`);
|
|
567
|
+
const title = deriveTitleFromPath(filePath);
|
|
568
|
+
const storyId = toStoryId(title, "default");
|
|
569
|
+
if (fs5.existsSync(storyPath)) {
|
|
570
|
+
res.setHeader("Content-Type", "application/json");
|
|
571
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
572
|
+
res.end(
|
|
573
|
+
JSON.stringify({
|
|
574
|
+
storyId,
|
|
575
|
+
storyPath,
|
|
576
|
+
importPath: `./${filePath.replace(ext, `/${AUTO_STORIES_FOLDER}/${baseName}.stories.tsx`)}`,
|
|
577
|
+
existed: true
|
|
578
|
+
})
|
|
579
|
+
);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
const storyContent = [
|
|
583
|
+
"import type { Meta, StoryObj } from '@storybook/react';",
|
|
584
|
+
`import ${baseName} from '../${baseName}';`,
|
|
585
|
+
"",
|
|
586
|
+
`const meta: Meta<typeof ${baseName}> = {`,
|
|
587
|
+
` title: '${title}',`,
|
|
588
|
+
` component: ${baseName},`,
|
|
589
|
+
"};",
|
|
590
|
+
"export default meta;",
|
|
591
|
+
"",
|
|
592
|
+
`type Story = StoryObj<typeof ${baseName}>;`,
|
|
593
|
+
"",
|
|
594
|
+
"export const Default: Story = {};",
|
|
595
|
+
""
|
|
596
|
+
].join("\n");
|
|
597
|
+
if (!fs5.existsSync(storyDir)) {
|
|
598
|
+
fs5.mkdirSync(storyDir, { recursive: true });
|
|
599
|
+
}
|
|
600
|
+
fs5.writeFileSync(storyPath, storyContent);
|
|
601
|
+
console.log(`[STORYBOOK_PLUGIN] Generated story: ${storyPath}`);
|
|
602
|
+
res.setHeader("Content-Type", "application/json");
|
|
603
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
604
|
+
res.end(
|
|
605
|
+
JSON.stringify({
|
|
606
|
+
storyId,
|
|
607
|
+
storyPath,
|
|
608
|
+
importPath: `./${path6.relative(process.cwd(), storyPath)}`,
|
|
609
|
+
existed: false
|
|
610
|
+
})
|
|
611
|
+
);
|
|
612
|
+
} catch (error) {
|
|
613
|
+
res.statusCode = 500;
|
|
614
|
+
res.setHeader("Content-Type", "application/json");
|
|
615
|
+
res.end(
|
|
616
|
+
JSON.stringify({
|
|
617
|
+
error: "Failed to generate story",
|
|
618
|
+
details: String(error)
|
|
619
|
+
})
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
if (req.method === "OPTIONS" && req.url?.startsWith("/onbook-")) {
|
|
626
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
627
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
628
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
629
|
+
res.statusCode = 204;
|
|
630
|
+
res.end();
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
502
633
|
if (req.url === "/onbook-index.json") {
|
|
503
634
|
console.log("[STORYBOOK_PLUGIN] Serving /onbook-index.json endpoint");
|
|
504
|
-
const manifestPath =
|
|
635
|
+
const manifestPath = path6.join(process.cwd(), ".storybook-cache", "manifest.json");
|
|
505
636
|
const cacheBuster = Date.now();
|
|
506
637
|
console.log("[STORYBOOK_PLUGIN] Fetching http://localhost:6006/index.json");
|
|
507
638
|
fetch(`http://localhost:6006/index.json?_t=${cacheBuster}`, {
|
|
@@ -586,7 +717,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
586
717
|
return;
|
|
587
718
|
}
|
|
588
719
|
if (req.url?.startsWith("/screenshots/")) {
|
|
589
|
-
const screenshotPath =
|
|
720
|
+
const screenshotPath = path6.join(
|
|
590
721
|
process.cwd(),
|
|
591
722
|
".storybook-cache",
|
|
592
723
|
req.url.replace("/screenshots/", "screenshots/")
|
|
@@ -607,7 +738,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
607
738
|
`[STORYBOOK_PLUGIN] Generating screenshot on-demand: ${storyId}/${theme}`
|
|
608
739
|
);
|
|
609
740
|
captureScreenshotBuffer(storyId, theme).then(({ buffer }) => {
|
|
610
|
-
const storyDir =
|
|
741
|
+
const storyDir = path6.join(
|
|
611
742
|
process.cwd(),
|
|
612
743
|
".storybook-cache",
|
|
613
744
|
"screenshots",
|
|
@@ -699,8 +830,8 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
699
830
|
};
|
|
700
831
|
const plugins = [componentLocPlugin(), mainPlugin];
|
|
701
832
|
if (options.autoStories !== false) {
|
|
702
|
-
|
|
703
|
-
|
|
833
|
+
discoveryConfig.imports = options.autoStories ?? ["src/**/*.tsx"];
|
|
834
|
+
discoveryConfig.ignores = options.autoStoriesIgnore ?? [
|
|
704
835
|
"src/**/*.stories.tsx",
|
|
705
836
|
"src/**/*.stories.ts",
|
|
706
837
|
"src/**/*.test.tsx",
|
|
@@ -710,27 +841,10 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
710
841
|
"node_modules/**",
|
|
711
842
|
"**/.onlook-stories/**"
|
|
712
843
|
];
|
|
713
|
-
console.log("[STORYBOOK_PLUGIN]
|
|
714
|
-
imports,
|
|
715
|
-
ignores
|
|
716
|
-
storiesFolder: AUTO_STORIES_FOLDER
|
|
844
|
+
console.log("[STORYBOOK_PLUGIN] Component discovery configured", {
|
|
845
|
+
imports: discoveryConfig.imports,
|
|
846
|
+
ignores: discoveryConfig.ignores
|
|
717
847
|
});
|
|
718
|
-
try {
|
|
719
|
-
plugins.push(
|
|
720
|
-
autoStoryGenerator.vite({
|
|
721
|
-
preset: "react",
|
|
722
|
-
imports,
|
|
723
|
-
ignores,
|
|
724
|
-
storiesFolder: AUTO_STORIES_FOLDER,
|
|
725
|
-
isGenerateStoriesFileAtBuild: true
|
|
726
|
-
})
|
|
727
|
-
);
|
|
728
|
-
} catch (err) {
|
|
729
|
-
console.error(
|
|
730
|
-
"[STORYBOOK_PLUGIN] ASG plugin failed to initialize, continuing without auto-stories",
|
|
731
|
-
err
|
|
732
|
-
);
|
|
733
|
-
}
|
|
734
848
|
}
|
|
735
849
|
return plugins;
|
|
736
850
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlook/storybook-plugin",
|
|
3
|
-
"version": "0.4.0-beta.
|
|
3
|
+
"version": "0.4.0-beta.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"onlook-storybook": "./dist/cli/index.js"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@babel/traverse": "^7.26.9",
|
|
38
38
|
"@babel/types": "^7.26.9",
|
|
39
39
|
"@drizzle-team/brocli": "^0.11.0",
|
|
40
|
-
"
|
|
40
|
+
"glob": "^11.0.0",
|
|
41
41
|
"playwright": "^1.52.0",
|
|
42
42
|
"react-docgen-typescript": "^2.4.0"
|
|
43
43
|
},
|