@onlook/storybook-plugin 0.4.0-beta.4 → 0.4.0-beta.6
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 +313 -328
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import fs2, { existsSync } from 'fs';
|
|
2
|
+
import path4, { dirname, join, relative } from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { minimatch } from 'minimatch';
|
|
5
|
-
import
|
|
6
|
-
import { Project, SyntaxKind, ts, TypeFlags } from 'ts-morph';
|
|
5
|
+
import { Project, ts, TypeFlags } from 'ts-morph';
|
|
7
6
|
import { glob } from 'glob';
|
|
8
7
|
import { withDefaultConfig } from 'react-docgen-typescript';
|
|
9
8
|
import generateModule from '@babel/generator';
|
|
@@ -15,7 +14,7 @@ import { chromium } from 'playwright';
|
|
|
15
14
|
|
|
16
15
|
// src/storybook-onlook-plugin.ts
|
|
17
16
|
function getComponentInfo(componentDir, projectRootDir) {
|
|
18
|
-
const fileParseInfo =
|
|
17
|
+
const fileParseInfo = path4.parse(componentDir);
|
|
19
18
|
const prefixExtRegex = new RegExp(`(\\.\\w+)+(?=\\${fileParseInfo.ext}$)`);
|
|
20
19
|
const prefixExtRegexMatch = fileParseInfo.base.match(prefixExtRegex);
|
|
21
20
|
const prefixExt = prefixExtRegexMatch ? prefixExtRegexMatch[0] : void 0;
|
|
@@ -187,55 +186,116 @@ var throwErr = (params) => {
|
|
|
187
186
|
console.error(`[ASG:${errorCode}] ${errorDefinition[errorCode].title}
|
|
188
187
|
${detailText}`);
|
|
189
188
|
};
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
storiesFolder
|
|
201
|
-
}) {
|
|
189
|
+
function genReactStoryFile(input) {
|
|
190
|
+
const {
|
|
191
|
+
componentName,
|
|
192
|
+
fileBase,
|
|
193
|
+
fileName,
|
|
194
|
+
filePrefixExt,
|
|
195
|
+
relativeSourceFilePath,
|
|
196
|
+
sourceFile,
|
|
197
|
+
storiesFolder
|
|
198
|
+
} = input;
|
|
202
199
|
if (!componentName || !fileBase) {
|
|
203
|
-
throwErr({
|
|
204
|
-
|
|
205
|
-
});
|
|
206
|
-
return;
|
|
200
|
+
throwErr({ errorCode: "EC03" });
|
|
201
|
+
return null;
|
|
207
202
|
}
|
|
208
|
-
const { propTypes } = getReactPropTypes({
|
|
209
|
-
sourceFile,
|
|
210
|
-
componentName
|
|
211
|
-
});
|
|
203
|
+
const { propTypes } = getReactPropTypes({ sourceFile, componentName });
|
|
212
204
|
const pascalComponentName = pascalCase(componentName);
|
|
213
205
|
if (!propTypes) {
|
|
214
|
-
throwErr({
|
|
215
|
-
|
|
216
|
-
});
|
|
217
|
-
return;
|
|
206
|
+
throwErr({ errorCode: "EC04" });
|
|
207
|
+
return null;
|
|
218
208
|
}
|
|
219
|
-
const
|
|
209
|
+
const exportedDeclarations = sourceFile.getExportedDeclarations();
|
|
220
210
|
let isDefaultExportComponent = false;
|
|
221
|
-
|
|
211
|
+
exportedDeclarations.forEach((declaration, exportName) => {
|
|
222
212
|
if (exportName === "default") {
|
|
223
213
|
const defaultExportName = declaration[0]?.getSymbol()?.getName();
|
|
224
214
|
isDefaultExportComponent = defaultExportName === pascalComponentName;
|
|
225
215
|
}
|
|
226
216
|
});
|
|
227
217
|
const pathToComponent = storiesFolder ? "../" : "./";
|
|
228
|
-
const
|
|
218
|
+
const importSuffix = filePrefixExt || "";
|
|
219
|
+
const importStatement = isDefaultExportComponent ? `import ${pascalComponentName} from "${pathToComponent}${fileName}${importSuffix}";` : `import { ${pascalComponentName} } from "${pathToComponent}${fileName}${importSuffix}";`;
|
|
220
|
+
const args = {};
|
|
221
|
+
for (const prop of propTypes) {
|
|
222
|
+
if (prop.isOptional) {
|
|
223
|
+
args[prop.name] = "undefined";
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (prop.type.includes("boolean")) {
|
|
227
|
+
args[prop.name] = "false";
|
|
228
|
+
} else if (prop.value.length > 0) {
|
|
229
|
+
args[prop.name] = `"${prop.value[0]}"`;
|
|
230
|
+
} else if (prop.type.includes("string") || prop.type.includes("String")) {
|
|
231
|
+
args[prop.name] = `"${prop.name}"`;
|
|
232
|
+
} else if (prop.type.includes("number") || prop.type.includes("Number")) {
|
|
233
|
+
args[prop.name] = "0";
|
|
234
|
+
} else {
|
|
235
|
+
args[prop.name] = "undefined";
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const argTypes = {};
|
|
239
|
+
for (const prop of propTypes) {
|
|
240
|
+
if (prop.type[0] === "boolean") {
|
|
241
|
+
argTypes[prop.name] = '{ control: "boolean" }';
|
|
242
|
+
} else if (prop.type[0] === "object") {
|
|
243
|
+
argTypes[prop.name] = '{ control: "object" }';
|
|
244
|
+
} else if (prop.value.length > 1) {
|
|
245
|
+
const options = prop.value.map((v) => `"${v}"`).join(", ");
|
|
246
|
+
argTypes[prop.name] = `{ control: "select", options: [${options}] }`;
|
|
247
|
+
} else if (prop.type[0] === "string") {
|
|
248
|
+
argTypes[prop.name] = '{ control: "text" }';
|
|
249
|
+
} else if (prop.type[0] === "number") {
|
|
250
|
+
argTypes[prop.name] = '{ control: "number" }';
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const argsStr = formatObjectEntries(args, " ");
|
|
254
|
+
const argTypesStr = formatObjectEntries(argTypes, " ");
|
|
255
|
+
const storyTitle = buildStoryTitle(relativeSourceFilePath, pascalComponentName);
|
|
256
|
+
return `import * as _React from "react";
|
|
229
257
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
258
|
+
${importStatement}
|
|
230
259
|
|
|
231
|
-
|
|
260
|
+
// Inline error boundary \u2014 catches render crashes from missing providers/context
|
|
261
|
+
class _StoryErrorBoundary extends _React.Component<
|
|
262
|
+
{ children: _React.ReactNode },
|
|
263
|
+
{ error: Error | null }
|
|
264
|
+
> {
|
|
265
|
+
state: { error: Error | null } = { error: null };
|
|
266
|
+
static getDerivedStateFromError(error: Error) { return { error }; }
|
|
267
|
+
render() {
|
|
268
|
+
if (this.state.error) {
|
|
269
|
+
return (
|
|
270
|
+
<div style={{ padding: 24, fontFamily: "system-ui", color: "#888", fontSize: 13 }}>
|
|
271
|
+
<div style={{ fontWeight: 600, marginBottom: 8, color: "#c44" }}>
|
|
272
|
+
Cannot render in isolation
|
|
273
|
+
</div>
|
|
274
|
+
<div style={{ fontSize: 12, opacity: 0.7 }}>
|
|
275
|
+
{this.state.error.message}
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
return this.props.children;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
232
283
|
|
|
233
284
|
const meta: Meta<typeof ${pascalComponentName}> = {
|
|
234
|
-
title: "
|
|
235
|
-
component:
|
|
285
|
+
title: "${storyTitle}",
|
|
286
|
+
component: ${pascalComponentName},
|
|
236
287
|
tags: ["autodocs"],
|
|
237
|
-
args: {},
|
|
238
|
-
argTypes: {},
|
|
288
|
+
args: {${argsStr}},
|
|
289
|
+
argTypes: {${argTypesStr}},
|
|
290
|
+
decorators: [
|
|
291
|
+
(Story) => (
|
|
292
|
+
<_StoryErrorBoundary>
|
|
293
|
+
<_React.Suspense fallback={<div style={{ padding: 24, color: "#888" }}>Loading...</div>}>
|
|
294
|
+
<Story />
|
|
295
|
+
</_React.Suspense>
|
|
296
|
+
</_StoryErrorBoundary>
|
|
297
|
+
),
|
|
298
|
+
],
|
|
239
299
|
};
|
|
240
300
|
|
|
241
301
|
export default meta;
|
|
@@ -243,66 +303,26 @@ type Story = StoryObj<typeof meta>;
|
|
|
243
303
|
|
|
244
304
|
export const Primary: Story = {};
|
|
245
305
|
`;
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (prop.value.length > 1) {
|
|
267
|
-
return argTypes[prop.name] = {
|
|
268
|
-
control: "select",
|
|
269
|
-
options: prop.value
|
|
270
|
-
};
|
|
271
|
-
} else {
|
|
272
|
-
if (prop.type[0] === "string") {
|
|
273
|
-
return argTypes[prop.name] = {
|
|
274
|
-
control: "text"
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
if (prop.type[0] === "number") {
|
|
278
|
-
return argTypes[prop.name] = {
|
|
279
|
-
control: "number"
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
return {
|
|
285
|
-
fileOptions: {
|
|
286
|
-
componentName,
|
|
287
|
-
fileBase,
|
|
288
|
-
fileName,
|
|
289
|
-
filePrefixExt,
|
|
290
|
-
path: path42,
|
|
291
|
-
fileExt,
|
|
292
|
-
relativeSourceFilePath,
|
|
293
|
-
sourceFile,
|
|
294
|
-
prettierConfigPath
|
|
295
|
-
},
|
|
296
|
-
generateOptions: {
|
|
297
|
-
fileExt: ".stories.tsx",
|
|
298
|
-
initialCode,
|
|
299
|
-
meta: {
|
|
300
|
-
component: componentCode,
|
|
301
|
-
args,
|
|
302
|
-
argTypes
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
};
|
|
306
|
+
}
|
|
307
|
+
function buildStoryTitle(relPath, componentName) {
|
|
308
|
+
let p = relPath.replace(/^src\//, "");
|
|
309
|
+
const lastSlash = p.lastIndexOf("/");
|
|
310
|
+
if (lastSlash !== -1) {
|
|
311
|
+
p = p.substring(0, lastSlash);
|
|
312
|
+
} else {
|
|
313
|
+
p = "";
|
|
314
|
+
}
|
|
315
|
+
p = p.replace(/\(([^)]+)\)/g, "$1");
|
|
316
|
+
p = p.replace(/\/+/g, "/").replace(/^\/|\/$/g, "");
|
|
317
|
+
return p ? `${p}/${componentName}` : componentName;
|
|
318
|
+
}
|
|
319
|
+
function formatObjectEntries(obj, indent) {
|
|
320
|
+
const entries = Object.entries(obj);
|
|
321
|
+
if (entries.length === 0) return "";
|
|
322
|
+
const lines = entries.map(([key, value]) => `${indent}${key}: ${value},`);
|
|
323
|
+
return `
|
|
324
|
+
${lines.join("\n")}
|
|
325
|
+
`;
|
|
306
326
|
}
|
|
307
327
|
function createLightProject() {
|
|
308
328
|
return new Project({
|
|
@@ -310,25 +330,12 @@ function createLightProject() {
|
|
|
310
330
|
useInMemoryFileSystem: true
|
|
311
331
|
});
|
|
312
332
|
}
|
|
313
|
-
var resolvedPrettierConfig = null;
|
|
314
|
-
async function getPrettierConfig(prettierConfigPath) {
|
|
315
|
-
if (resolvedPrettierConfig) return resolvedPrettierConfig;
|
|
316
|
-
resolvedPrettierConfig = prettierConfigPath ? await prettier.resolveConfig(prettierConfigPath) : {
|
|
317
|
-
semi: true,
|
|
318
|
-
trailingComma: "all",
|
|
319
|
-
singleQuote: false,
|
|
320
|
-
printWidth: 80,
|
|
321
|
-
tabWidth: 2,
|
|
322
|
-
endOfLine: "lf"
|
|
323
|
-
};
|
|
324
|
-
return resolvedPrettierConfig ?? {};
|
|
325
|
-
}
|
|
326
333
|
async function genStoryFile({
|
|
327
334
|
options,
|
|
328
335
|
id,
|
|
329
336
|
projectRootDir
|
|
330
337
|
}) {
|
|
331
|
-
if (id.includes(".stories")) return;
|
|
338
|
+
if (id.includes(".stories")) return null;
|
|
332
339
|
const {
|
|
333
340
|
fileBase,
|
|
334
341
|
fileName,
|
|
@@ -338,145 +345,45 @@ async function genStoryFile({
|
|
|
338
345
|
relativeSourceFilePath
|
|
339
346
|
} = getComponentInfo(id, projectRootDir);
|
|
340
347
|
try {
|
|
341
|
-
const sourceCode =
|
|
348
|
+
const sourceCode = fs2.readFileSync(id, "utf-8");
|
|
342
349
|
const sourceProject = createLightProject();
|
|
343
350
|
const sourceFile = sourceProject.createSourceFile(fileBase || "temp.tsx", sourceCode);
|
|
344
|
-
|
|
345
|
-
if (options.preset === "react") {
|
|
346
|
-
genStoryFileOptions = await genReactStoryFile({
|
|
347
|
-
componentName,
|
|
348
|
-
fileBase,
|
|
349
|
-
fileName,
|
|
350
|
-
path: id,
|
|
351
|
-
fileExt,
|
|
352
|
-
filePrefixExt,
|
|
353
|
-
relativeSourceFilePath,
|
|
354
|
-
sourceFile,
|
|
355
|
-
prettierConfigPath: options.prettierConfigPath,
|
|
356
|
-
storiesFolder: options.storiesFolder
|
|
357
|
-
});
|
|
358
|
-
} else {
|
|
351
|
+
if (options.preset !== "react") {
|
|
359
352
|
throwErr({
|
|
360
353
|
errorCode: "EC02",
|
|
361
354
|
detail: `Preset ${options.preset} is not supported in this fork. Only "react" is supported.`
|
|
362
355
|
});
|
|
363
356
|
return;
|
|
364
357
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
358
|
+
const storyCode = genReactStoryFile({
|
|
359
|
+
componentName,
|
|
360
|
+
fileBase,
|
|
361
|
+
fileName,
|
|
362
|
+
path: id,
|
|
363
|
+
fileExt,
|
|
364
|
+
filePrefixExt,
|
|
365
|
+
relativeSourceFilePath,
|
|
366
|
+
sourceFile,
|
|
367
|
+
storiesFolder: options.storiesFolder
|
|
368
|
+
});
|
|
369
|
+
if (!storyCode) return null;
|
|
370
|
+
const parsed = path4.parse(id);
|
|
371
|
+
const storyFileName = `${fileName}.stories.tsx`;
|
|
372
|
+
let storiesFilePath;
|
|
374
373
|
if (options.storiesFolder) {
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
);
|
|
381
|
-
storiesFolderPath = storiesFilePath.replace(
|
|
382
|
-
`${genStoryFileOptions.fileOptions.fileName}${genStoryFileOptions.generateOptions.fileExt}`,
|
|
383
|
-
options.storiesFolder || ""
|
|
384
|
-
);
|
|
385
|
-
}
|
|
386
|
-
const storiesFilePathFinal = options.storiesFolder ? storiesFilePathWithStoriesFolder : storiesFilePath;
|
|
387
|
-
const storyExists = fs.existsSync(storiesFilePathFinal);
|
|
388
|
-
if (!storyExists) {
|
|
389
|
-
if (options.storiesFolder) {
|
|
390
|
-
fs.mkdirSync(storiesFolderPath, { recursive: true });
|
|
391
|
-
}
|
|
392
|
-
fs.writeFileSync(
|
|
393
|
-
storiesFilePathFinal,
|
|
394
|
-
genStoryFileOptions.generateOptions.initialCode
|
|
395
|
-
);
|
|
396
|
-
}
|
|
397
|
-
const storiesProject = new Project();
|
|
398
|
-
const storiesSourceFile = storiesProject.addSourceFileAtPath(storiesFilePathFinal);
|
|
399
|
-
const meta = storiesSourceFile.getVariableDeclaration("meta");
|
|
400
|
-
if (!meta || !meta.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
401
|
-
throwErr({
|
|
402
|
-
errorCode: "EC05",
|
|
403
|
-
detail: `Could not find meta in file ${storiesSourceFile.getFilePath()}`
|
|
404
|
-
});
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
const initializer = meta.getInitializerIfKindOrThrow(
|
|
408
|
-
SyntaxKind.ObjectLiteralExpression
|
|
409
|
-
);
|
|
410
|
-
if (!initializer) {
|
|
411
|
-
throwErr({ errorCode: "EC06" });
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
if (genStoryFileOptions.generateOptions.meta.render) {
|
|
415
|
-
let renderProperty = initializer.getProperty("render");
|
|
416
|
-
while (!renderProperty) {
|
|
417
|
-
initializer.addPropertyAssignment({
|
|
418
|
-
name: "render",
|
|
419
|
-
initializer: "() => {}"
|
|
420
|
-
});
|
|
421
|
-
renderProperty = initializer.getProperty("render");
|
|
422
|
-
}
|
|
423
|
-
renderProperty.set({
|
|
424
|
-
initializer: genStoryFileOptions.generateOptions.meta.render
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
if (genStoryFileOptions.generateOptions.meta.component) {
|
|
428
|
-
let componentProperty = initializer.getProperty("component");
|
|
429
|
-
while (!componentProperty) {
|
|
430
|
-
initializer.addPropertyAssignment({
|
|
431
|
-
name: "component",
|
|
432
|
-
initializer: "null"
|
|
433
|
-
});
|
|
434
|
-
componentProperty = initializer.getProperty("component");
|
|
435
|
-
}
|
|
436
|
-
componentProperty.set({
|
|
437
|
-
initializer: genStoryFileOptions.generateOptions.meta.component
|
|
438
|
-
});
|
|
439
|
-
}
|
|
440
|
-
if (genStoryFileOptions.generateOptions.meta.args) {
|
|
441
|
-
let argsProperty = initializer.getProperty("args");
|
|
442
|
-
while (!argsProperty) {
|
|
443
|
-
initializer.addPropertyAssignment({
|
|
444
|
-
name: "args",
|
|
445
|
-
initializer: "{}"
|
|
446
|
-
});
|
|
447
|
-
argsProperty = initializer.getProperty("args");
|
|
448
|
-
}
|
|
449
|
-
const argText = Object.entries(genStoryFileOptions.generateOptions.meta.args).map((x) => x.join(":")).join(", ");
|
|
450
|
-
argsProperty.set({ initializer: `{ ${argText} }` });
|
|
374
|
+
const storiesFolderPath = path4.join(parsed.dir, options.storiesFolder);
|
|
375
|
+
fs2.mkdirSync(storiesFolderPath, { recursive: true });
|
|
376
|
+
storiesFilePath = path4.join(storiesFolderPath, storyFileName);
|
|
377
|
+
} else {
|
|
378
|
+
storiesFilePath = path4.join(parsed.dir, storyFileName);
|
|
451
379
|
}
|
|
452
|
-
if (
|
|
453
|
-
|
|
454
|
-
while (!argTypesProperty) {
|
|
455
|
-
initializer.addPropertyAssignment({
|
|
456
|
-
name: "argTypes",
|
|
457
|
-
initializer: "{}"
|
|
458
|
-
});
|
|
459
|
-
argTypesProperty = initializer.getProperty("argTypes");
|
|
460
|
-
}
|
|
461
|
-
const argTypesText = JSON.stringify(
|
|
462
|
-
genStoryFileOptions.generateOptions.meta.argTypes,
|
|
463
|
-
null,
|
|
464
|
-
""
|
|
465
|
-
);
|
|
466
|
-
argTypesProperty.set({ initializer: `${argTypesText}` });
|
|
380
|
+
if (!fs2.existsSync(storiesFilePath)) {
|
|
381
|
+
fs2.writeFileSync(storiesFilePath, storyCode);
|
|
467
382
|
}
|
|
468
|
-
|
|
469
|
-
const fileContent = fs.readFileSync(storiesFilePathFinal, "utf-8");
|
|
470
|
-
const config = await getPrettierConfig(
|
|
471
|
-
genStoryFileOptions.fileOptions.prettierConfigPath
|
|
472
|
-
);
|
|
473
|
-
const formattedContent = await prettier.format(fileContent, {
|
|
474
|
-
...config,
|
|
475
|
-
parser: "typescript"
|
|
476
|
-
});
|
|
477
|
-
fs.writeFileSync(storiesFilePathFinal, formattedContent);
|
|
383
|
+
return storiesFilePath;
|
|
478
384
|
} catch (err) {
|
|
479
385
|
console.warn(`[ASG] Failed to generate story for ${id}:`, err);
|
|
386
|
+
return null;
|
|
480
387
|
}
|
|
481
388
|
}
|
|
482
389
|
async function getAllFilePaths({
|
|
@@ -485,10 +392,10 @@ async function getAllFilePaths({
|
|
|
485
392
|
projectRootDir
|
|
486
393
|
}) {
|
|
487
394
|
const fullPatterns = patterns.map(
|
|
488
|
-
(p) =>
|
|
395
|
+
(p) => path4.join(projectRootDir, p).replace(/\\/g, "/")
|
|
489
396
|
);
|
|
490
397
|
const ignoreFullPatterns = ignorePatterns?.map(
|
|
491
|
-
(p) =>
|
|
398
|
+
(p) => path4.join(projectRootDir, p).replace(/\\/g, "/")
|
|
492
399
|
);
|
|
493
400
|
const filePaths = await glob(fullPatterns, {
|
|
494
401
|
ignore: ignoreFullPatterns,
|
|
@@ -497,12 +404,37 @@ async function getAllFilePaths({
|
|
|
497
404
|
return filePaths.map((p) => p.replace(/\\/g, "/"));
|
|
498
405
|
}
|
|
499
406
|
var PLUGIN_NAME = "auto-story-generator";
|
|
500
|
-
var DEFAULT_BATCH_SIZE =
|
|
501
|
-
var DEFAULT_CONCURRENCY =
|
|
407
|
+
var DEFAULT_BATCH_SIZE = 50;
|
|
408
|
+
var DEFAULT_CONCURRENCY = 8;
|
|
409
|
+
var CACHE_FILE_NAME = ".asg-cache.json";
|
|
502
410
|
var mtimeCache = /* @__PURE__ */ new Map();
|
|
411
|
+
function loadDiskCache(projectRootDir) {
|
|
412
|
+
try {
|
|
413
|
+
const cachePath = path4.join(projectRootDir, CACHE_FILE_NAME);
|
|
414
|
+
if (fs2.existsSync(cachePath)) {
|
|
415
|
+
const data = JSON.parse(fs2.readFileSync(cachePath, "utf-8"));
|
|
416
|
+
for (const [key, value] of Object.entries(data)) {
|
|
417
|
+
mtimeCache.set(key, value);
|
|
418
|
+
}
|
|
419
|
+
console.log(`[ASG] Loaded ${mtimeCache.size} entries from disk cache`);
|
|
420
|
+
}
|
|
421
|
+
} catch {
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function saveDiskCache(projectRootDir) {
|
|
425
|
+
try {
|
|
426
|
+
const cachePath = path4.join(projectRootDir, CACHE_FILE_NAME);
|
|
427
|
+
const data = {};
|
|
428
|
+
for (const [key, value] of mtimeCache) {
|
|
429
|
+
data[key] = value;
|
|
430
|
+
}
|
|
431
|
+
fs2.writeFileSync(cachePath, JSON.stringify(data));
|
|
432
|
+
} catch {
|
|
433
|
+
}
|
|
434
|
+
}
|
|
503
435
|
function hasFileChanged(filePath) {
|
|
504
436
|
try {
|
|
505
|
-
const stat =
|
|
437
|
+
const stat = fs2.statSync(filePath);
|
|
506
438
|
const mtime = stat.mtimeMs;
|
|
507
439
|
const cached = mtimeCache.get(filePath);
|
|
508
440
|
if (cached === mtime) return false;
|
|
@@ -514,24 +446,25 @@ function hasFileChanged(filePath) {
|
|
|
514
446
|
}
|
|
515
447
|
}
|
|
516
448
|
function getStoryFilePath(filePath, storiesFolder) {
|
|
517
|
-
const parsed =
|
|
449
|
+
const parsed = path4.parse(filePath);
|
|
518
450
|
const storyName = `${parsed.name}.stories.tsx`;
|
|
519
451
|
if (storiesFolder) {
|
|
520
|
-
return
|
|
452
|
+
return path4.join(parsed.dir, storiesFolder, storyName);
|
|
521
453
|
}
|
|
522
|
-
return
|
|
454
|
+
return path4.join(parsed.dir, storyName);
|
|
523
455
|
}
|
|
524
|
-
async function processBatch(files, options, projectRootDir, concurrency) {
|
|
456
|
+
async function processBatch(files, options, projectRootDir, concurrency, generatedFiles) {
|
|
525
457
|
let processed = 0;
|
|
526
458
|
for (let i = 0; i < files.length; i += concurrency) {
|
|
527
459
|
const chunk = files.slice(i, i + concurrency);
|
|
528
460
|
await Promise.all(
|
|
529
461
|
chunk.map(async (filePath) => {
|
|
530
|
-
await genStoryFile({
|
|
462
|
+
const result = await genStoryFile({
|
|
531
463
|
options,
|
|
532
464
|
id: filePath,
|
|
533
465
|
projectRootDir
|
|
534
466
|
});
|
|
467
|
+
if (result) generatedFiles.push(result);
|
|
535
468
|
processed++;
|
|
536
469
|
})
|
|
537
470
|
);
|
|
@@ -542,6 +475,8 @@ function createAutoStoryPlugin(options) {
|
|
|
542
475
|
const projectRootDir = (options.projectRoot ?? process.cwd()).replace(/\\/g, "/");
|
|
543
476
|
const batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
544
477
|
const concurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
|
|
478
|
+
let hasRun = false;
|
|
479
|
+
const allGeneratedFiles = [];
|
|
545
480
|
console.log("[ASG] Plugin created", {
|
|
546
481
|
preset: options.preset,
|
|
547
482
|
imports: options.imports,
|
|
@@ -550,47 +485,77 @@ function createAutoStoryPlugin(options) {
|
|
|
550
485
|
projectRoot: projectRootDir,
|
|
551
486
|
cwd: process.cwd()
|
|
552
487
|
});
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
488
|
+
async function generateAllStories() {
|
|
489
|
+
if (hasRun) return;
|
|
490
|
+
hasRun = true;
|
|
491
|
+
if (!options.isGenerateStoriesFileAtBuild) {
|
|
492
|
+
console.log("[ASG] Skipping \u2014 isGenerateStoriesFileAtBuild is false");
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
loadDiskCache(projectRootDir);
|
|
496
|
+
const patterns = options.imports ?? ["src/**/*.tsx"];
|
|
497
|
+
const ignorePatterns = options.ignores ?? [];
|
|
498
|
+
console.log(`[ASG] Scanning for components: ${patterns.join(", ")}`);
|
|
499
|
+
const allFiles = await getAllFilePaths({
|
|
500
|
+
patterns,
|
|
501
|
+
ignorePatterns,
|
|
502
|
+
projectRootDir
|
|
503
|
+
});
|
|
504
|
+
const filesToProcess = allFiles.filter((filePath) => {
|
|
505
|
+
if (filePath.includes(".stories")) return false;
|
|
506
|
+
if (options.cacheEnabled !== false) {
|
|
507
|
+
const storyPath = getStoryFilePath(filePath, options.storiesFolder);
|
|
508
|
+
const storyExists = fs2.existsSync(storyPath);
|
|
509
|
+
if (!hasFileChanged(filePath) && storyExists) return false;
|
|
560
510
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
}
|
|
576
|
-
return true;
|
|
577
|
-
});
|
|
578
|
-
console.log(
|
|
579
|
-
`[ASG] Found ${allFiles.length} files, ${filesToProcess.length} need processing`
|
|
511
|
+
return true;
|
|
512
|
+
});
|
|
513
|
+
console.log(
|
|
514
|
+
`[ASG] Found ${allFiles.length} files, ${filesToProcess.length} need processing`
|
|
515
|
+
);
|
|
516
|
+
let totalProcessed = 0;
|
|
517
|
+
for (let i = 0; i < filesToProcess.length; i += batchSize) {
|
|
518
|
+
const batch = filesToProcess.slice(i, i + batchSize);
|
|
519
|
+
const count = await processBatch(
|
|
520
|
+
batch,
|
|
521
|
+
options,
|
|
522
|
+
projectRootDir,
|
|
523
|
+
concurrency,
|
|
524
|
+
allGeneratedFiles
|
|
580
525
|
);
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
526
|
+
totalProcessed += count;
|
|
527
|
+
if (options.onProgress) {
|
|
528
|
+
options.onProgress(totalProcessed, filesToProcess.length);
|
|
529
|
+
}
|
|
530
|
+
if (i + batchSize < filesToProcess.length) {
|
|
531
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
saveDiskCache(projectRootDir);
|
|
535
|
+
console.log(`[ASG] Generated stories for ${totalProcessed} components`);
|
|
536
|
+
}
|
|
537
|
+
return {
|
|
538
|
+
name: PLUGIN_NAME,
|
|
539
|
+
// configureServer fires for Vite dev server (Storybook's use case)
|
|
540
|
+
// biome-ignore lint/suspicious/noExplicitAny: Vite server type varies across versions
|
|
541
|
+
async configureServer(server) {
|
|
542
|
+
console.log("[ASG] configureServer hook fired");
|
|
543
|
+
const beforeCount = allGeneratedFiles.length;
|
|
544
|
+
await generateAllStories();
|
|
545
|
+
if (allGeneratedFiles.length > beforeCount && allGeneratedFiles.length > 0) {
|
|
546
|
+
const firstFile = allGeneratedFiles[0];
|
|
547
|
+
if (firstFile && server?.watcher?.emit) {
|
|
548
|
+
console.log(
|
|
549
|
+
`[ASG] Triggering Storybook rescan (${allGeneratedFiles.length} stories)`
|
|
550
|
+
);
|
|
551
|
+
server.watcher.emit("change", firstFile);
|
|
591
552
|
}
|
|
592
553
|
}
|
|
593
|
-
|
|
554
|
+
},
|
|
555
|
+
// buildStart fires for builds (fallback)
|
|
556
|
+
async buildStart() {
|
|
557
|
+
console.log("[ASG] buildStart hook fired");
|
|
558
|
+
await generateAllStories();
|
|
594
559
|
},
|
|
595
560
|
async watchChange(id, change) {
|
|
596
561
|
if (change.event === "delete") return;
|
|
@@ -629,12 +594,12 @@ var parser = withDefaultConfig({
|
|
|
629
594
|
}
|
|
630
595
|
});
|
|
631
596
|
function resolveComponentPath(storyFilePath) {
|
|
632
|
-
const dir =
|
|
633
|
-
const parentDir =
|
|
634
|
-
const storyName =
|
|
597
|
+
const dir = path4.dirname(storyFilePath);
|
|
598
|
+
const parentDir = path4.dirname(dir);
|
|
599
|
+
const storyName = path4.basename(storyFilePath);
|
|
635
600
|
const componentName = storyName.replace(".stories.tsx", ".tsx");
|
|
636
|
-
const componentPath =
|
|
637
|
-
return
|
|
601
|
+
const componentPath = path4.join(parentDir, componentName);
|
|
602
|
+
return fs2.existsSync(componentPath) ? componentPath : null;
|
|
638
603
|
}
|
|
639
604
|
function generateArgTypes(componentPath) {
|
|
640
605
|
try {
|
|
@@ -675,7 +640,7 @@ function generateArgTypes(componentPath) {
|
|
|
675
640
|
}
|
|
676
641
|
function enrichStoryFile(storyFilePath) {
|
|
677
642
|
try {
|
|
678
|
-
const content =
|
|
643
|
+
const content = fs2.readFileSync(storyFilePath, "utf-8");
|
|
679
644
|
if (content.includes(FIXED_MARKER)) {
|
|
680
645
|
return;
|
|
681
646
|
}
|
|
@@ -694,8 +659,8 @@ function enrichStoryFile(storyFilePath) {
|
|
|
694
659
|
export default meta;`
|
|
695
660
|
);
|
|
696
661
|
if (enriched !== content) {
|
|
697
|
-
|
|
698
|
-
console.log(`[AutoStories] Enriched ${
|
|
662
|
+
fs2.writeFileSync(storyFilePath, enriched);
|
|
663
|
+
console.log(`[AutoStories] Enriched ${path4.basename(storyFilePath)} with argTypes`);
|
|
699
664
|
}
|
|
700
665
|
} catch (err) {
|
|
701
666
|
console.error(`[AutoStories] Failed to enrich story: ${storyFilePath}`, err);
|
|
@@ -723,7 +688,7 @@ function componentLocPlugin(options = {}) {
|
|
|
723
688
|
sourceFilename: filepath
|
|
724
689
|
});
|
|
725
690
|
let mutated = false;
|
|
726
|
-
const relativePath =
|
|
691
|
+
const relativePath = path4.relative(root, filepath);
|
|
727
692
|
traverse(ast, {
|
|
728
693
|
JSXElement(nodePath) {
|
|
729
694
|
const opening = nodePath.node.openingElement;
|
|
@@ -760,9 +725,9 @@ function componentLocPlugin(options = {}) {
|
|
|
760
725
|
}
|
|
761
726
|
};
|
|
762
727
|
}
|
|
763
|
-
var CACHE_DIR =
|
|
764
|
-
var SCREENSHOTS_DIR =
|
|
765
|
-
var MANIFEST_PATH =
|
|
728
|
+
var CACHE_DIR = path4.join(process.cwd(), ".storybook-cache");
|
|
729
|
+
var SCREENSHOTS_DIR = path4.join(CACHE_DIR, "screenshots");
|
|
730
|
+
var MANIFEST_PATH = path4.join(CACHE_DIR, "manifest.json");
|
|
766
731
|
var VIEWPORT_WIDTH = 1920;
|
|
767
732
|
var VIEWPORT_HEIGHT = 1080;
|
|
768
733
|
var MIN_COMPONENT_WIDTH = 420;
|
|
@@ -770,30 +735,30 @@ var MIN_COMPONENT_HEIGHT = 280;
|
|
|
770
735
|
|
|
771
736
|
// src/utils/fileSystem/fileSystem.ts
|
|
772
737
|
function ensureCacheDirectories() {
|
|
773
|
-
if (!
|
|
774
|
-
|
|
738
|
+
if (!fs2.existsSync(CACHE_DIR)) {
|
|
739
|
+
fs2.mkdirSync(CACHE_DIR, { recursive: true });
|
|
775
740
|
}
|
|
776
|
-
if (!
|
|
777
|
-
|
|
741
|
+
if (!fs2.existsSync(SCREENSHOTS_DIR)) {
|
|
742
|
+
fs2.mkdirSync(SCREENSHOTS_DIR, { recursive: true });
|
|
778
743
|
}
|
|
779
744
|
}
|
|
780
745
|
function computeFileHash(filePath) {
|
|
781
|
-
if (!
|
|
746
|
+
if (!fs2.existsSync(filePath)) {
|
|
782
747
|
return "";
|
|
783
748
|
}
|
|
784
|
-
const content =
|
|
749
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
785
750
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
786
751
|
}
|
|
787
752
|
function loadManifest() {
|
|
788
|
-
if (
|
|
789
|
-
const content =
|
|
753
|
+
if (fs2.existsSync(MANIFEST_PATH)) {
|
|
754
|
+
const content = fs2.readFileSync(MANIFEST_PATH, "utf-8");
|
|
790
755
|
return JSON.parse(content);
|
|
791
756
|
}
|
|
792
757
|
return { stories: {} };
|
|
793
758
|
}
|
|
794
759
|
function saveManifest(manifest) {
|
|
795
760
|
ensureCacheDirectories();
|
|
796
|
-
|
|
761
|
+
fs2.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
797
762
|
}
|
|
798
763
|
function updateManifest(storyId, sourcePath, fileHash, boundingBox) {
|
|
799
764
|
const manifest = loadManifest();
|
|
@@ -819,8 +784,8 @@ async function getBrowser() {
|
|
|
819
784
|
return browser;
|
|
820
785
|
}
|
|
821
786
|
function getScreenshotPath(storyId, theme) {
|
|
822
|
-
const storyDir =
|
|
823
|
-
return
|
|
787
|
+
const storyDir = path4.join(SCREENSHOTS_DIR, storyId);
|
|
788
|
+
return path4.join(storyDir, `${theme}.png`);
|
|
824
789
|
}
|
|
825
790
|
async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, height = VIEWPORT_HEIGHT, storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
826
791
|
const browser2 = await getBrowser();
|
|
@@ -907,9 +872,9 @@ async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, h
|
|
|
907
872
|
async function generateScreenshot(storyId, theme, storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
908
873
|
try {
|
|
909
874
|
ensureCacheDirectories();
|
|
910
|
-
const storyDir =
|
|
911
|
-
if (!
|
|
912
|
-
|
|
875
|
+
const storyDir = path4.join(SCREENSHOTS_DIR, storyId);
|
|
876
|
+
if (!fs2.existsSync(storyDir)) {
|
|
877
|
+
fs2.mkdirSync(storyDir, { recursive: true });
|
|
913
878
|
}
|
|
914
879
|
const screenshotPath = getScreenshotPath(storyId, theme);
|
|
915
880
|
const { buffer, boundingBox } = await captureScreenshotBuffer(
|
|
@@ -920,7 +885,7 @@ async function generateScreenshot(storyId, theme, storybookUrl = "http://localho
|
|
|
920
885
|
storybookUrl,
|
|
921
886
|
timeoutMs
|
|
922
887
|
);
|
|
923
|
-
|
|
888
|
+
fs2.writeFileSync(screenshotPath, buffer);
|
|
924
889
|
return { path: screenshotPath, boundingBox };
|
|
925
890
|
} catch (error) {
|
|
926
891
|
console.error(`Error generating screenshot for ${storyId} (${theme}):`, error);
|
|
@@ -947,7 +912,7 @@ async function fetchStorybookIndex() {
|
|
|
947
912
|
}
|
|
948
913
|
function getStoriesForFile(filePath) {
|
|
949
914
|
if (!cachedIndex) return [];
|
|
950
|
-
const fileName =
|
|
915
|
+
const fileName = path4.basename(filePath);
|
|
951
916
|
return Object.values(cachedIndex.entries).filter((entry) => entry.type === "story" && entry.importPath.endsWith(fileName)).map((entry) => entry.id);
|
|
952
917
|
}
|
|
953
918
|
async function regenerateScreenshotsForFiles(files) {
|
|
@@ -1109,7 +1074,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1109
1074
|
}
|
|
1110
1075
|
if (req.url === "/onbook-index.json") {
|
|
1111
1076
|
console.log("[STORYBOOK_PLUGIN] Serving /onbook-index.json endpoint");
|
|
1112
|
-
const manifestPath =
|
|
1077
|
+
const manifestPath = path4.join(process.cwd(), ".storybook-cache", "manifest.json");
|
|
1113
1078
|
const cacheBuster = Date.now();
|
|
1114
1079
|
console.log("[STORYBOOK_PLUGIN] Fetching http://localhost:6006/index.json");
|
|
1115
1080
|
fetch(`http://localhost:6006/index.json?_t=${cacheBuster}`, {
|
|
@@ -1124,9 +1089,20 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1124
1089
|
ok: response.ok,
|
|
1125
1090
|
statusText: response.statusText
|
|
1126
1091
|
});
|
|
1092
|
+
if (!response.ok) {
|
|
1093
|
+
console.log("[STORYBOOK_PLUGIN] Index not ready yet", {
|
|
1094
|
+
status: response.status
|
|
1095
|
+
});
|
|
1096
|
+
res.statusCode = 503;
|
|
1097
|
+
res.setHeader("Content-Type", "application/json");
|
|
1098
|
+
res.setHeader("Retry-After", "10");
|
|
1099
|
+
res.end(JSON.stringify({ code: "INDEX_NOT_READY" }));
|
|
1100
|
+
return null;
|
|
1101
|
+
}
|
|
1127
1102
|
return response.json();
|
|
1128
1103
|
}).then((indexData) => {
|
|
1129
|
-
|
|
1104
|
+
if (!indexData) return;
|
|
1105
|
+
const manifest = fs2.existsSync(manifestPath) ? JSON.parse(fs2.readFileSync(manifestPath, "utf-8")) : { stories: {} };
|
|
1130
1106
|
const defaultBoundingBox = { width: 1920, height: 1080 };
|
|
1131
1107
|
for (const [storyId, entry] of Object.entries(indexData.entries || {})) {
|
|
1132
1108
|
const manifestEntry = manifest.stories?.[storyId];
|
|
@@ -1144,15 +1120,17 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1144
1120
|
res.setHeader("Expires", "0");
|
|
1145
1121
|
res.end(JSON.stringify(indexData));
|
|
1146
1122
|
}).catch((error) => {
|
|
1147
|
-
console.
|
|
1148
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1149
|
-
errorType: error instanceof Error ? error.constructor.name : typeof error,
|
|
1150
|
-
stack: error instanceof Error ? error.stack : void 0
|
|
1123
|
+
console.log("[STORYBOOK_PLUGIN] Index not available", {
|
|
1124
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1151
1125
|
});
|
|
1152
|
-
res.statusCode =
|
|
1126
|
+
res.statusCode = 503;
|
|
1153
1127
|
res.setHeader("Content-Type", "application/json");
|
|
1128
|
+
res.setHeader("Retry-After", "10");
|
|
1154
1129
|
res.end(
|
|
1155
|
-
JSON.stringify({
|
|
1130
|
+
JSON.stringify({
|
|
1131
|
+
code: "INDEX_NOT_READY",
|
|
1132
|
+
details: String(error)
|
|
1133
|
+
})
|
|
1156
1134
|
);
|
|
1157
1135
|
});
|
|
1158
1136
|
return;
|
|
@@ -1194,7 +1172,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1194
1172
|
return;
|
|
1195
1173
|
}
|
|
1196
1174
|
if (req.url?.startsWith("/screenshots/")) {
|
|
1197
|
-
const screenshotPath =
|
|
1175
|
+
const screenshotPath = path4.join(
|
|
1198
1176
|
process.cwd(),
|
|
1199
1177
|
".storybook-cache",
|
|
1200
1178
|
req.url.replace("/screenshots/", "screenshots/")
|
|
@@ -1203,11 +1181,11 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1203
1181
|
const storyId = urlParts[0];
|
|
1204
1182
|
const themeFile = urlParts[1];
|
|
1205
1183
|
const theme = themeFile?.replace(".png", "");
|
|
1206
|
-
if (
|
|
1184
|
+
if (fs2.existsSync(screenshotPath)) {
|
|
1207
1185
|
res.setHeader("Content-Type", "image/png");
|
|
1208
1186
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1209
1187
|
res.setHeader("Cache-Control", "public, max-age=3600");
|
|
1210
|
-
|
|
1188
|
+
fs2.createReadStream(screenshotPath).pipe(res);
|
|
1211
1189
|
return;
|
|
1212
1190
|
}
|
|
1213
1191
|
if (storyId && theme && (theme === "light" || theme === "dark")) {
|
|
@@ -1215,16 +1193,16 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
1215
1193
|
`[STORYBOOK_PLUGIN] Generating screenshot on-demand: ${storyId}/${theme}`
|
|
1216
1194
|
);
|
|
1217
1195
|
captureScreenshotBuffer(storyId, theme).then(({ buffer }) => {
|
|
1218
|
-
const storyDir =
|
|
1196
|
+
const storyDir = path4.join(
|
|
1219
1197
|
process.cwd(),
|
|
1220
1198
|
".storybook-cache",
|
|
1221
1199
|
"screenshots",
|
|
1222
1200
|
storyId
|
|
1223
1201
|
);
|
|
1224
|
-
if (!
|
|
1225
|
-
|
|
1202
|
+
if (!fs2.existsSync(storyDir)) {
|
|
1203
|
+
fs2.mkdirSync(storyDir, { recursive: true });
|
|
1226
1204
|
}
|
|
1227
|
-
|
|
1205
|
+
fs2.writeFileSync(screenshotPath, buffer);
|
|
1228
1206
|
res.setHeader("Content-Type", "image/png");
|
|
1229
1207
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1230
1208
|
res.setHeader("Cache-Control", "public, max-age=3600");
|
|
@@ -1316,7 +1294,14 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
1316
1294
|
"src/**/*.spec.tsx",
|
|
1317
1295
|
"src/**/*.spec.ts",
|
|
1318
1296
|
"node_modules/**",
|
|
1319
|
-
"**/.onlook-stories/**"
|
|
1297
|
+
"**/.onlook-stories/**",
|
|
1298
|
+
// Next.js route files (not renderable as Storybook stories)
|
|
1299
|
+
"src/**/page.tsx",
|
|
1300
|
+
"src/**/layout.tsx",
|
|
1301
|
+
"src/**/loading.tsx",
|
|
1302
|
+
"src/**/error.tsx",
|
|
1303
|
+
"src/**/not-found.tsx",
|
|
1304
|
+
"src/**/template.tsx"
|
|
1320
1305
|
];
|
|
1321
1306
|
console.log("[STORYBOOK_PLUGIN] Auto-story generation enabled", {
|
|
1322
1307
|
imports,
|
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.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"onlook-storybook": "./dist/cli/index.js"
|
|
@@ -40,7 +40,6 @@
|
|
|
40
40
|
"glob": "^10.3.10",
|
|
41
41
|
"minimatch": "^9.0.3",
|
|
42
42
|
"playwright": "^1.52.0",
|
|
43
|
-
"prettier": "^3.2.5",
|
|
44
43
|
"react-docgen-typescript": "^2.4.0",
|
|
45
44
|
"ts-morph": "^21.0.1"
|
|
46
45
|
},
|