@onlook/storybook-plugin 0.4.0-beta.1 → 0.4.0-beta.10
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 +1 -1
- package/dist/cli/index.js +199 -52
- package/dist/index.d.ts +94 -21
- package/dist/index.js +835 -235
- package/dist/preset/index.d.ts +10 -0
- package/dist/preset/index.js +1345 -0
- package/dist/preview/index.d.ts +156 -0
- package/dist/preview/index.js +302 -0
- package/dist/screenshot-service/index.d.ts +0 -6
- package/dist/screenshot-service/index.js +197 -50
- package/dist/screenshot-service/utils/browser/index.d.ts +0 -6
- package/dist/screenshot-service/utils/browser/index.js +83 -14
- package/dist/storybook-onlook-plugin-CigILdDb.d.ts +61 -0
- package/package.json +19 -9
package/dist/index.js
CHANGED
|
@@ -1,97 +1,309 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import path6, { dirname, join, relative } from 'path';
|
|
3
|
+
import crypto, { createHash } from 'crypto';
|
|
4
|
+
import fs4, { existsSync } from 'fs';
|
|
3
5
|
import { fileURLToPath } from 'url';
|
|
4
|
-
import autoStoryGenerator from '@takuma-ru/auto-story-generator';
|
|
5
|
-
import { withDefaultConfig } from 'react-docgen-typescript';
|
|
6
6
|
import generateModule from '@babel/generator';
|
|
7
7
|
import { parse } from '@babel/parser';
|
|
8
8
|
import traverseModule from '@babel/traverse';
|
|
9
9
|
import * as t from '@babel/types';
|
|
10
|
-
import
|
|
10
|
+
import { execFile } from 'child_process';
|
|
11
|
+
import { createRequire as createRequire$1 } from 'module';
|
|
12
|
+
import { promisify } from 'util';
|
|
11
13
|
import { chromium } from 'playwright';
|
|
14
|
+
import { readCsf } from 'storybook/internal/csf-tools';
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
16
|
+
globalThis.require = createRequire(import.meta.url);
|
|
17
|
+
|
|
18
|
+
// src/containment-contract/containment-contract.ts
|
|
19
|
+
var ONLOOK_CONTAINMENT_CONTRACT_VERSION = 1;
|
|
20
|
+
var ONLOOK_CONTAINED_MODULES_GLOBAL = "__ONLOOK_CONTAINED_MODULES__";
|
|
21
|
+
var ONLOOK_CONTAINED_PROP = "__onlookContained";
|
|
22
|
+
var ONLOOK_CONTAINMENT_CONTRACT_GLOBAL = "__ONLOOK_CONTAINMENT_CONTRACT__";
|
|
23
|
+
var ONLOOK_CONTAINED_EVENT = "onlook/contained";
|
|
24
|
+
var ONLOOK_RENDERED_EVENT = "onlook/rendered";
|
|
25
|
+
var ONLOOK_UNCONTAINED_ERROR_EVENT = "onlook/uncontained-error";
|
|
26
|
+
var ONLOOK_RENDER_STATE_ATTR = "data-onlook-render-state";
|
|
27
|
+
var ONLOOK_RENDER_STORY_ATTR = "data-onlook-story-id";
|
|
28
|
+
var ONLOOK_CONTAINED_CARD_ATTR = "data-onlook-contained-card";
|
|
29
|
+
var ENV_CONTAINMENT_PLUGIN_NAME = "onlook-env-containment";
|
|
30
|
+
var ENV_CONTAINMENT_VIRTUAL_PREFIX = "\0onlook-env-containment:";
|
|
31
|
+
var VALID_IDENT_RE = /^[A-Za-z_$][\w$]*$/;
|
|
32
|
+
var MATCH_SUFFIXES = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
33
|
+
function normalizeSlashes(p) {
|
|
34
|
+
return p.replace(/\\/g, "/");
|
|
35
|
+
}
|
|
36
|
+
function stripQuery(id) {
|
|
37
|
+
const q = id.indexOf("?");
|
|
38
|
+
return q === -1 ? id : id.slice(0, q);
|
|
39
|
+
}
|
|
40
|
+
function envContainmentPlugin(options) {
|
|
41
|
+
const substitutions = (options?.substitute ?? []).filter(
|
|
42
|
+
(s) => typeof s.module === "string" && s.module.length > 0
|
|
43
|
+
);
|
|
44
|
+
const defineEntries = options?.define ?? {};
|
|
45
|
+
if (substitutions.length === 0 && Object.keys(defineEntries).length === 0) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const byModule = /* @__PURE__ */ new Map();
|
|
49
|
+
for (const sub of substitutions) {
|
|
50
|
+
byModule.set(normalizeSlashes(sub.module).replace(/^\.\//, ""), sub);
|
|
51
|
+
}
|
|
52
|
+
const targetBasenames = /* @__PURE__ */ new Set();
|
|
53
|
+
for (const rel of byModule.keys()) {
|
|
54
|
+
const base = rel.slice(rel.lastIndexOf("/") + 1);
|
|
55
|
+
targetBasenames.add(base.replace(/\.[^.]+$/, ""));
|
|
56
|
+
}
|
|
57
|
+
const specifierMightMatch = (cleaned) => {
|
|
58
|
+
const segment = cleaned.slice(cleaned.lastIndexOf("/") + 1);
|
|
59
|
+
return targetBasenames.has(segment.replace(/\.[^.]+$/, ""));
|
|
60
|
+
};
|
|
61
|
+
const resolveOutcomes = /* @__PURE__ */ new Map();
|
|
62
|
+
const roots = /* @__PURE__ */ new Set([normalizeSlashes(process.cwd())]);
|
|
63
|
+
const matchTarget = (absPath) => {
|
|
64
|
+
const norm = normalizeSlashes(path6.normalize(absPath));
|
|
65
|
+
for (const rel of byModule.keys()) {
|
|
66
|
+
for (const root of roots) {
|
|
67
|
+
const target = `${root}/${rel}`;
|
|
68
|
+
for (const suffix of MATCH_SUFFIXES) {
|
|
69
|
+
if (`${norm}${suffix}` === target) return rel;
|
|
70
|
+
}
|
|
45
71
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
name: ENV_CONTAINMENT_PLUGIN_NAME,
|
|
77
|
+
// `pre` so we see specifiers before Vite's alias/resolve pipeline and
|
|
78
|
+
// before other resolveId hooks (e.g. tsconfig-paths) claim them.
|
|
79
|
+
enforce: "pre",
|
|
80
|
+
config() {
|
|
81
|
+
const keys = Object.entries(defineEntries);
|
|
82
|
+
if (keys.length === 0) return {};
|
|
83
|
+
const define = {};
|
|
84
|
+
for (const [key, value] of keys) {
|
|
85
|
+
const serialized = JSON.stringify(value);
|
|
86
|
+
define[`process.env.${key}`] = serialized;
|
|
87
|
+
define[`import.meta.env.${key}`] = serialized;
|
|
51
88
|
}
|
|
52
|
-
|
|
53
|
-
|
|
89
|
+
return { define };
|
|
90
|
+
},
|
|
91
|
+
configResolved(config) {
|
|
92
|
+
if (config.root) roots.add(normalizeSlashes(config.root));
|
|
93
|
+
},
|
|
94
|
+
async resolveId(source, importer) {
|
|
95
|
+
if (byModule.size === 0) return null;
|
|
96
|
+
if (source.startsWith("\0")) return null;
|
|
97
|
+
const cleaned = stripQuery(source);
|
|
98
|
+
let direct = null;
|
|
99
|
+
if (path6.isAbsolute(cleaned)) {
|
|
100
|
+
direct = cleaned;
|
|
101
|
+
} else if (importer && (cleaned.startsWith("./") || cleaned.startsWith("../"))) {
|
|
102
|
+
direct = path6.resolve(path6.dirname(stripQuery(importer)), cleaned);
|
|
54
103
|
}
|
|
55
|
-
if (
|
|
56
|
-
|
|
104
|
+
if (direct) {
|
|
105
|
+
const rel = matchTarget(direct);
|
|
106
|
+
if (rel) return ENV_CONTAINMENT_VIRTUAL_PREFIX + rel;
|
|
107
|
+
return null;
|
|
57
108
|
}
|
|
58
|
-
if (
|
|
59
|
-
|
|
109
|
+
if (!specifierMightMatch(cleaned)) return null;
|
|
110
|
+
if (typeof this.resolve !== "function") return null;
|
|
111
|
+
const importerDir = importer ? path6.dirname(stripQuery(importer)) : "";
|
|
112
|
+
const outcomeKey = `${importerDir}|${source}`;
|
|
113
|
+
const memoized = resolveOutcomes.get(outcomeKey);
|
|
114
|
+
if (memoized !== void 0) return memoized;
|
|
115
|
+
const resolved = await this.resolve(source, importer, { skipSelf: true });
|
|
116
|
+
let outcome = null;
|
|
117
|
+
if (resolved?.id && !resolved.external) {
|
|
118
|
+
const rel = matchTarget(stripQuery(resolved.id));
|
|
119
|
+
if (rel) outcome = ENV_CONTAINMENT_VIRTUAL_PREFIX + rel;
|
|
60
120
|
}
|
|
121
|
+
resolveOutcomes.set(outcomeKey, outcome);
|
|
122
|
+
return outcome;
|
|
123
|
+
},
|
|
124
|
+
load(id) {
|
|
125
|
+
if (!id.startsWith(ENV_CONTAINMENT_VIRTUAL_PREFIX)) return null;
|
|
126
|
+
const rel = id.slice(ENV_CONTAINMENT_VIRTUAL_PREFIX.length);
|
|
127
|
+
const sub = byModule.get(rel);
|
|
128
|
+
if (!sub) return null;
|
|
129
|
+
return buildStandInSource(sub);
|
|
61
130
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function buildStandInSource(sub) {
|
|
134
|
+
const marker = {
|
|
135
|
+
modulePath: sub.module,
|
|
136
|
+
reason: "env-validation",
|
|
137
|
+
envKeys: sub.envKeys ?? [],
|
|
138
|
+
...sub.schemaKind ? { schemaKind: sub.schemaKind } : {}
|
|
139
|
+
};
|
|
140
|
+
const named = Array.from(
|
|
141
|
+
new Set(
|
|
142
|
+
(sub.exports ?? []).filter(
|
|
143
|
+
(name) => VALID_IDENT_RE.test(name) && name !== "default"
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
);
|
|
147
|
+
return [
|
|
148
|
+
"// Generated by @onlook/storybook-plugin (env containment).",
|
|
149
|
+
`// The real module (${sub.module}) validates environment variables at`,
|
|
150
|
+
"// module scope and throws on import when keys are missing. This stand-in",
|
|
151
|
+
"// defers the throw to property access so Storybook can boot and the",
|
|
152
|
+
"// Onlook error boundary can attribute the failure to the module.",
|
|
153
|
+
`const __onlookMarker = ${JSON.stringify(marker)};`,
|
|
154
|
+
"const __onlookGlobal = globalThis;",
|
|
155
|
+
`__onlookGlobal.${ONLOOK_CONTAINED_MODULES_GLOBAL} =`,
|
|
156
|
+
` __onlookGlobal.${ONLOOK_CONTAINED_MODULES_GLOBAL} || {};`,
|
|
157
|
+
`__onlookGlobal.${ONLOOK_CONTAINED_MODULES_GLOBAL}[__onlookMarker.modulePath] = __onlookMarker;`,
|
|
158
|
+
"function __onlookContainedError(access) {",
|
|
159
|
+
" const keys = __onlookMarker.envKeys.length",
|
|
160
|
+
" ? ' Missing env keys: ' + __onlookMarker.envKeys.join(', ') + '.'",
|
|
161
|
+
" : '';",
|
|
162
|
+
" const err = new Error(",
|
|
163
|
+
" '[onlook] ' + __onlookMarker.modulePath +",
|
|
164
|
+
" ' was contained: it validates environment variables at module scope' +",
|
|
165
|
+
" ' and the required keys are not set. Accessed: ' + access + '.' + keys,",
|
|
166
|
+
" );",
|
|
167
|
+
` err.${ONLOOK_CONTAINED_PROP} = __onlookMarker;`,
|
|
168
|
+
" return err;",
|
|
169
|
+
"}",
|
|
170
|
+
"function __onlookStandIn(exportName) {",
|
|
171
|
+
" return new Proxy(function () {}, {",
|
|
172
|
+
" get(_target, prop) {",
|
|
173
|
+
` if (prop === '${ONLOOK_CONTAINED_PROP}') return __onlookMarker;`,
|
|
174
|
+
" if (typeof prop === 'symbol' || prop === 'then') return undefined;",
|
|
175
|
+
" throw __onlookContainedError(exportName + '.' + String(prop));",
|
|
176
|
+
" },",
|
|
177
|
+
" apply() {",
|
|
178
|
+
" throw __onlookContainedError(exportName + '()');",
|
|
179
|
+
" },",
|
|
180
|
+
" construct() {",
|
|
181
|
+
" throw __onlookContainedError('new ' + exportName + '()');",
|
|
182
|
+
" },",
|
|
183
|
+
" });",
|
|
184
|
+
"}",
|
|
185
|
+
"export default __onlookStandIn('default');",
|
|
186
|
+
...named.map(
|
|
187
|
+
(name) => `export const ${name} = __onlookStandIn(${JSON.stringify(name)});`
|
|
188
|
+
),
|
|
189
|
+
""
|
|
190
|
+
].join("\n");
|
|
67
191
|
}
|
|
68
|
-
|
|
192
|
+
|
|
193
|
+
// src/module-load-catch-all/module-load-catch-all.ts
|
|
194
|
+
var STORYBOOK_VIRTUAL_STORIES_ID = "virtual:/@storybook/builder-vite/storybook-stories.js";
|
|
195
|
+
var RESOLVED_STORYBOOK_VIRTUAL_STORIES_ID = `\0${STORYBOOK_VIRTUAL_STORIES_ID}`;
|
|
196
|
+
var MODULE_LOAD_CATCH_ALL_PLUGIN_NAME = "onlook-module-load-catch-all";
|
|
197
|
+
var IMPORT_FN_CALL = "return await importers[path]();";
|
|
198
|
+
var INJECTED_BINDING = "__onlookSyntheticCsfModule";
|
|
199
|
+
async function onlookSyntheticCsfModule(importPath, loadError, containedProp) {
|
|
200
|
+
const original = loadError instanceof Error ? loadError : new Error(String(loadError));
|
|
201
|
+
const synthetic = new Error(
|
|
202
|
+
`Story module failed to load: ${importPath}
|
|
203
|
+
${original.message}`
|
|
204
|
+
);
|
|
205
|
+
const marker = original[containedProp];
|
|
206
|
+
if (marker !== void 0) {
|
|
207
|
+
synthetic[containedProp] = marker;
|
|
208
|
+
}
|
|
209
|
+
synthetic.cause = original;
|
|
69
210
|
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
if (content.includes("argTypes:")) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const componentPath = resolveComponentPath(storyFilePath);
|
|
78
|
-
if (!componentPath) return;
|
|
79
|
-
const argTypes = generateArgTypes(componentPath);
|
|
80
|
-
if (!argTypes) return;
|
|
81
|
-
const argTypesStr = JSON.stringify(argTypes, null, 2).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
|
|
82
|
-
const enriched = content.replace(
|
|
83
|
-
/};\s*\nexport default meta;/,
|
|
84
|
-
` argTypes: ${argTypesStr},
|
|
85
|
-
};
|
|
86
|
-
export default meta;`
|
|
211
|
+
console.error(
|
|
212
|
+
"[ONLOOK] Story module failed to load \u2014 serving contained synthetic stories.",
|
|
213
|
+
{ importPath, error: original }
|
|
87
214
|
);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
215
|
+
} catch {
|
|
216
|
+
}
|
|
217
|
+
const stories = [];
|
|
218
|
+
try {
|
|
219
|
+
const response = await fetch("./index.json");
|
|
220
|
+
if (response?.ok) {
|
|
221
|
+
const index = await response.json();
|
|
222
|
+
for (const entry of Object.values(index.entries ?? {})) {
|
|
223
|
+
if (!entry || entry.type !== "story") continue;
|
|
224
|
+
if (entry.importPath !== importPath) continue;
|
|
225
|
+
if (typeof entry.id !== "string") continue;
|
|
226
|
+
stories.push({
|
|
227
|
+
id: entry.id,
|
|
228
|
+
name: typeof entry.name === "string" ? entry.name : entry.id
|
|
229
|
+
});
|
|
230
|
+
}
|
|
91
231
|
}
|
|
92
|
-
} catch
|
|
93
|
-
|
|
232
|
+
} catch {
|
|
233
|
+
}
|
|
234
|
+
if (stories.length === 0) {
|
|
235
|
+
try {
|
|
236
|
+
const search = typeof window !== "undefined" && window.location ? window.location.search : "";
|
|
237
|
+
const urlId = new URLSearchParams(search).get("id");
|
|
238
|
+
if (urlId) stories.push({ id: urlId, name: "Needs setup" });
|
|
239
|
+
} catch {
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const moduleExports = { default: {} };
|
|
243
|
+
const render = () => {
|
|
244
|
+
throw synthetic;
|
|
245
|
+
};
|
|
246
|
+
if (stories.length === 0) {
|
|
247
|
+
moduleExports.OnlookContained = { name: "Needs setup", render };
|
|
248
|
+
return moduleExports;
|
|
249
|
+
}
|
|
250
|
+
let i = 0;
|
|
251
|
+
for (const story of stories) {
|
|
252
|
+
moduleExports[`OnlookContained${i}`] = {
|
|
253
|
+
name: story.name,
|
|
254
|
+
// Forces the exact index story id — export names can't be recovered
|
|
255
|
+
// from a module that never evaluated.
|
|
256
|
+
parameters: { __id: story.id },
|
|
257
|
+
render
|
|
258
|
+
};
|
|
259
|
+
i += 1;
|
|
94
260
|
}
|
|
261
|
+
return moduleExports;
|
|
262
|
+
}
|
|
263
|
+
function wrapImportFn(code) {
|
|
264
|
+
if (code.includes(INJECTED_BINDING)) return null;
|
|
265
|
+
if (!code.includes(IMPORT_FN_CALL)) return null;
|
|
266
|
+
const wrappedCall = [
|
|
267
|
+
"try {",
|
|
268
|
+
" return await importers[path]();",
|
|
269
|
+
" } catch (__onlookLoadError) {",
|
|
270
|
+
` return await ${INJECTED_BINDING}(path, __onlookLoadError, ${JSON.stringify(ONLOOK_CONTAINED_PROP)});`,
|
|
271
|
+
" }"
|
|
272
|
+
].join("\n");
|
|
273
|
+
const body = code.replace(IMPORT_FN_CALL, wrappedCall);
|
|
274
|
+
return [
|
|
275
|
+
body,
|
|
276
|
+
"",
|
|
277
|
+
"// Injected by @onlook/storybook-plugin (module-load catch-all, U7).",
|
|
278
|
+
`const ${INJECTED_BINDING} = ${onlookSyntheticCsfModule.toString()};`,
|
|
279
|
+
""
|
|
280
|
+
].join("\n");
|
|
281
|
+
}
|
|
282
|
+
function moduleLoadCatchAllPlugin() {
|
|
283
|
+
let warnedDrift = false;
|
|
284
|
+
return {
|
|
285
|
+
name: MODULE_LOAD_CATCH_ALL_PLUGIN_NAME,
|
|
286
|
+
// 'pre' so the wrap sees builder-vite's raw codegen (served from its
|
|
287
|
+
// `load` hook) before normal-phase transforms (e.g. plugin-react, whose
|
|
288
|
+
// default filter matches the virtual id's `.js` suffix) reshape it.
|
|
289
|
+
enforce: "pre",
|
|
290
|
+
transform(code, id) {
|
|
291
|
+
if (id !== RESOLVED_STORYBOOK_VIRTUAL_STORIES_ID && id !== STORYBOOK_VIRTUAL_STORIES_ID) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const wrapped = wrapImportFn(code);
|
|
295
|
+
if (wrapped === null) {
|
|
296
|
+
if (!warnedDrift && !code.includes(INJECTED_BINDING)) {
|
|
297
|
+
warnedDrift = true;
|
|
298
|
+
console.warn(
|
|
299
|
+
"[STORYBOOK_PLUGIN] module-load-catch-all: unrecognized importFn codegen \u2014 passing through (module-load failures will surface as raw Storybook overlays)."
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
return { code: wrapped, map: null };
|
|
305
|
+
}
|
|
306
|
+
};
|
|
95
307
|
}
|
|
96
308
|
function componentLocPlugin(options = {}) {
|
|
97
309
|
const include = options.include ?? /\.(jsx|tsx)$/;
|
|
@@ -109,13 +321,14 @@ function componentLocPlugin(options = {}) {
|
|
|
109
321
|
const filepath = id.split("?", 1)[0];
|
|
110
322
|
if (!filepath || filepath.includes("node_modules")) return null;
|
|
111
323
|
if (!include.test(filepath)) return null;
|
|
324
|
+
if (/\.stories\.(jsx?|tsx?)$/.test(filepath)) return null;
|
|
112
325
|
const ast = parse(code, {
|
|
113
326
|
sourceType: "module",
|
|
114
327
|
plugins: ["jsx", "typescript"],
|
|
115
328
|
sourceFilename: filepath
|
|
116
329
|
});
|
|
117
330
|
let mutated = false;
|
|
118
|
-
const relativePath =
|
|
331
|
+
const relativePath = path6.relative(root, filepath);
|
|
119
332
|
traverse(ast, {
|
|
120
333
|
JSXElement(nodePath) {
|
|
121
334
|
const opening = nodePath.node.openingElement;
|
|
@@ -152,9 +365,31 @@ function componentLocPlugin(options = {}) {
|
|
|
152
365
|
}
|
|
153
366
|
};
|
|
154
367
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
368
|
+
|
|
369
|
+
// src/fast-refresh-tolerant-exports/fast-refresh-tolerant-exports.ts
|
|
370
|
+
function fastRefreshTolerantExportsPlugin() {
|
|
371
|
+
const REGISTER_CALL_RE = /(\w+)\.registerExportsForReactRefresh\((["'])([^"'\n]+)\2,\s*([A-Za-z_$][\w$]*)\)/;
|
|
372
|
+
return {
|
|
373
|
+
name: "onbook-fast-refresh-tolerant-exports",
|
|
374
|
+
enforce: "post",
|
|
375
|
+
apply: "serve",
|
|
376
|
+
transform(code, id) {
|
|
377
|
+
if (!/\.(tsx|jsx)$/.test(id)) return null;
|
|
378
|
+
if (id.includes("node_modules")) return null;
|
|
379
|
+
const m = code.match(REGISTER_CALL_RE);
|
|
380
|
+
if (!m) return null;
|
|
381
|
+
const [fullMatch, , , idValue, exportsVar] = m;
|
|
382
|
+
if (!idValue || !exportsVar) return null;
|
|
383
|
+
const idLiteral = JSON.stringify(idValue);
|
|
384
|
+
const postRegister = `;(()=>{if(typeof window==='undefined')return;const M=(window.__onl1176_nonComponentExports||=Object.create(null));const isLikelyComponentType=(t)=>{switch(typeof t){case 'function':{if(t.prototype!=null){if(t.prototype.isReactComponent)return true;const o=Object.getOwnPropertyNames(t.prototype);if(o.length>1||o[0]!=='constructor')return false;if(Object.getPrototypeOf(t.prototype)!==Object.prototype)return false;}const n=t.name||t.displayName;return typeof n==='string'&&/^[A-Z]/.test(n);}case 'object':{if(t==null)return false;const s=t.$$typeof;return s===Symbol.for('react.forward_ref')||s===Symbol.for('react.memo');}default:return false;}};const isPlainObject=(o)=>Object.prototype.toString.call(o)==='[object Object]'&&(o.constructor===Object||o.constructor===undefined);const isCompoundComponent=(t)=>{if(!isPlainObject(t))return false;for(const k in t)if(!isLikelyComponentType(t[k]))return false;return true;};const isComp=(v)=>isLikelyComponentType(v)||isCompoundComponent(v);const ig=[];for(const k in ${exportsVar}){if(k==='__esModule')continue;if(!isComp(${exportsVar}[k]))ig.push(k);}M[` + idLiteral + "]=ig;if(!window.__getReactRefreshIgnoredExports){window.__getReactRefreshIgnoredExports=({id})=>window.__onl1176_nonComponentExports?.[id]||[];}})();";
|
|
385
|
+
const out = code.replace(fullMatch, fullMatch + postRegister);
|
|
386
|
+
return out === code ? null : { code: out, map: null };
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
var CACHE_DIR = path6.join(process.cwd(), ".storybook-cache");
|
|
391
|
+
var SCREENSHOTS_DIR = path6.join(CACHE_DIR, "screenshots");
|
|
392
|
+
var MANIFEST_PATH = path6.join(CACHE_DIR, "manifest.json");
|
|
158
393
|
var VIEWPORT_WIDTH = 1920;
|
|
159
394
|
var VIEWPORT_HEIGHT = 1080;
|
|
160
395
|
var MIN_COMPONENT_WIDTH = 420;
|
|
@@ -162,30 +397,30 @@ var MIN_COMPONENT_HEIGHT = 280;
|
|
|
162
397
|
|
|
163
398
|
// src/utils/fileSystem/fileSystem.ts
|
|
164
399
|
function ensureCacheDirectories() {
|
|
165
|
-
if (!
|
|
166
|
-
|
|
400
|
+
if (!fs4.existsSync(CACHE_DIR)) {
|
|
401
|
+
fs4.mkdirSync(CACHE_DIR, { recursive: true });
|
|
167
402
|
}
|
|
168
|
-
if (!
|
|
169
|
-
|
|
403
|
+
if (!fs4.existsSync(SCREENSHOTS_DIR)) {
|
|
404
|
+
fs4.mkdirSync(SCREENSHOTS_DIR, { recursive: true });
|
|
170
405
|
}
|
|
171
406
|
}
|
|
172
407
|
function computeFileHash(filePath) {
|
|
173
|
-
if (!
|
|
408
|
+
if (!fs4.existsSync(filePath)) {
|
|
174
409
|
return "";
|
|
175
410
|
}
|
|
176
|
-
const content =
|
|
411
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
177
412
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
178
413
|
}
|
|
179
414
|
function loadManifest() {
|
|
180
|
-
if (
|
|
181
|
-
const content =
|
|
415
|
+
if (fs4.existsSync(MANIFEST_PATH)) {
|
|
416
|
+
const content = fs4.readFileSync(MANIFEST_PATH, "utf-8");
|
|
182
417
|
return JSON.parse(content);
|
|
183
418
|
}
|
|
184
419
|
return { stories: {} };
|
|
185
420
|
}
|
|
186
421
|
function saveManifest(manifest) {
|
|
187
422
|
ensureCacheDirectories();
|
|
188
|
-
|
|
423
|
+
fs4.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
|
|
189
424
|
}
|
|
190
425
|
function updateManifest(storyId, sourcePath, fileHash, boundingBox) {
|
|
191
426
|
const manifest = loadManifest();
|
|
@@ -201,22 +436,86 @@ function updateManifest(storyId, sourcePath, fileHash, boundingBox) {
|
|
|
201
436
|
};
|
|
202
437
|
saveManifest(manifest);
|
|
203
438
|
}
|
|
204
|
-
var
|
|
439
|
+
var execFileAsync = promisify(execFile);
|
|
440
|
+
var require2 = createRequire$1(import.meta.url);
|
|
441
|
+
var browserPromise = null;
|
|
442
|
+
var installPromise = null;
|
|
443
|
+
var isMissingExecutableError = (err) => {
|
|
444
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
445
|
+
return msg.includes("Executable doesn't exist") || msg.includes("Please run the following command to download new browsers") || // Older playwright revisions phrase the version-mismatch case differently.
|
|
446
|
+
/chromium-\d+\/chrome-linux\/chrome/i.test(msg);
|
|
447
|
+
};
|
|
448
|
+
function resolvePlaywrightCli() {
|
|
449
|
+
const pkgJson = require2.resolve("playwright-core/package.json");
|
|
450
|
+
return join(dirname(pkgJson), "cli.js");
|
|
451
|
+
}
|
|
452
|
+
async function installChromium() {
|
|
453
|
+
if (!installPromise) {
|
|
454
|
+
installPromise = (async () => {
|
|
455
|
+
const cliPath = resolvePlaywrightCli();
|
|
456
|
+
console.log(
|
|
457
|
+
`[STORYBOOK_PLUGIN] chromium binary missing \u2014 installing via ${cliPath}`
|
|
458
|
+
);
|
|
459
|
+
try {
|
|
460
|
+
const { stdout, stderr } = await execFileAsync(
|
|
461
|
+
process.execPath,
|
|
462
|
+
[cliPath, "install", "--force", "chromium"],
|
|
463
|
+
{ timeout: 5 * 60 * 1e3, maxBuffer: 32 * 1024 * 1024 }
|
|
464
|
+
);
|
|
465
|
+
if (stdout)
|
|
466
|
+
console.log(`[STORYBOOK_PLUGIN] playwright install: ${stdout.slice(-1e3)}`);
|
|
467
|
+
if (stderr)
|
|
468
|
+
console.log(
|
|
469
|
+
`[STORYBOOK_PLUGIN] playwright install stderr: ${stderr.slice(-1e3)}`
|
|
470
|
+
);
|
|
471
|
+
console.log("[STORYBOOK_PLUGIN] chromium installed");
|
|
472
|
+
} catch (err) {
|
|
473
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
474
|
+
console.error(`[STORYBOOK_PLUGIN] chromium install FAILED: ${detail}`);
|
|
475
|
+
throw new Error(`Playwright chromium install failed: ${detail}`);
|
|
476
|
+
} finally {
|
|
477
|
+
installPromise = null;
|
|
478
|
+
}
|
|
479
|
+
})();
|
|
480
|
+
}
|
|
481
|
+
return installPromise;
|
|
482
|
+
}
|
|
483
|
+
async function launchWithSelfHeal() {
|
|
484
|
+
try {
|
|
485
|
+
return await chromium.launch({ headless: true });
|
|
486
|
+
} catch (err) {
|
|
487
|
+
if (!isMissingExecutableError(err)) throw err;
|
|
488
|
+
console.log("[STORYBOOK_PLUGIN] launch failed, attempting self-heal install");
|
|
489
|
+
await installChromium();
|
|
490
|
+
try {
|
|
491
|
+
return await chromium.launch({ headless: true });
|
|
492
|
+
} catch (postInstallErr) {
|
|
493
|
+
const detail = postInstallErr instanceof Error ? postInstallErr.message : String(postInstallErr);
|
|
494
|
+
console.error(
|
|
495
|
+
`[STORYBOOK_PLUGIN] launch still failing after install \u2014 likely path or libs issue: ${detail}`
|
|
496
|
+
);
|
|
497
|
+
throw new Error(
|
|
498
|
+
`Playwright chromium is unavailable in this sandbox (install ran but launch still failed): ${detail}`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
205
503
|
async function getBrowser() {
|
|
206
|
-
if (!
|
|
207
|
-
|
|
208
|
-
|
|
504
|
+
if (!browserPromise) {
|
|
505
|
+
browserPromise = launchWithSelfHeal().catch((err) => {
|
|
506
|
+
browserPromise = null;
|
|
507
|
+
throw err;
|
|
209
508
|
});
|
|
210
509
|
}
|
|
211
|
-
return
|
|
510
|
+
return browserPromise;
|
|
212
511
|
}
|
|
213
512
|
function getScreenshotPath(storyId, theme) {
|
|
214
|
-
const storyDir =
|
|
215
|
-
return
|
|
513
|
+
const storyDir = path6.join(SCREENSHOTS_DIR, storyId);
|
|
514
|
+
return path6.join(storyDir, `${theme}.png`);
|
|
216
515
|
}
|
|
217
516
|
async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, height = VIEWPORT_HEIGHT, storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
218
|
-
const
|
|
219
|
-
const context = await
|
|
517
|
+
const browser = await getBrowser();
|
|
518
|
+
const context = await browser.newContext({
|
|
220
519
|
viewport: { width, height },
|
|
221
520
|
deviceScaleFactor: 2
|
|
222
521
|
});
|
|
@@ -299,9 +598,9 @@ async function captureScreenshotBuffer(storyId, theme, width = VIEWPORT_WIDTH, h
|
|
|
299
598
|
async function generateScreenshot(storyId, theme, storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
300
599
|
try {
|
|
301
600
|
ensureCacheDirectories();
|
|
302
|
-
const storyDir =
|
|
303
|
-
if (!
|
|
304
|
-
|
|
601
|
+
const storyDir = path6.join(SCREENSHOTS_DIR, storyId);
|
|
602
|
+
if (!fs4.existsSync(storyDir)) {
|
|
603
|
+
fs4.mkdirSync(storyDir, { recursive: true });
|
|
305
604
|
}
|
|
306
605
|
const screenshotPath = getScreenshotPath(storyId, theme);
|
|
307
606
|
const { buffer, boundingBox } = await captureScreenshotBuffer(
|
|
@@ -312,7 +611,7 @@ async function generateScreenshot(storyId, theme, storybookUrl = "http://localho
|
|
|
312
611
|
storybookUrl,
|
|
313
612
|
timeoutMs
|
|
314
613
|
);
|
|
315
|
-
|
|
614
|
+
fs4.writeFileSync(screenshotPath, buffer);
|
|
316
615
|
return { path: screenshotPath, boundingBox };
|
|
317
616
|
} catch (error) {
|
|
318
617
|
console.error(`Error generating screenshot for ${storyId} (${theme}):`, error);
|
|
@@ -320,12 +619,93 @@ async function generateScreenshot(storyId, theme, storybookUrl = "http://localho
|
|
|
320
619
|
}
|
|
321
620
|
}
|
|
322
621
|
|
|
622
|
+
// src/screenshot-service/screenshot-service.ts
|
|
623
|
+
process.env.ONBOOK_PROXY_TOKEN ?? null;
|
|
624
|
+
process.env.ONBOOK_BROADCAST_URL ?? null;
|
|
625
|
+
|
|
626
|
+
// src/utils/notifyOnbookIndexChanged/notifyOnbookIndexChanged.ts
|
|
627
|
+
var DEBOUNCE_MS = 300;
|
|
628
|
+
var REQUEST_TIMEOUT_MS = 5e3;
|
|
629
|
+
var pending = null;
|
|
630
|
+
var inFlight = false;
|
|
631
|
+
var pendingTrailingCall = false;
|
|
632
|
+
function notifyOnbookIndexChanged() {
|
|
633
|
+
const proxyToken = process.env.ONBOOK_PROXY_TOKEN;
|
|
634
|
+
const broadcastUrl = process.env.ONBOOK_BROADCAST_URL;
|
|
635
|
+
if (!proxyToken || !broadcastUrl) return;
|
|
636
|
+
if (pending) clearTimeout(pending);
|
|
637
|
+
pending = setTimeout(() => {
|
|
638
|
+
pending = null;
|
|
639
|
+
if (inFlight) {
|
|
640
|
+
pendingTrailingCall = true;
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
void fire(proxyToken, broadcastUrl);
|
|
644
|
+
}, DEBOUNCE_MS);
|
|
645
|
+
}
|
|
646
|
+
async function fire(proxyToken, broadcastUrl) {
|
|
647
|
+
inFlight = true;
|
|
648
|
+
try {
|
|
649
|
+
const res = await fetch(broadcastUrl, {
|
|
650
|
+
method: "POST",
|
|
651
|
+
headers: {
|
|
652
|
+
Authorization: `Bearer ${proxyToken}`,
|
|
653
|
+
"Content-Type": "application/json"
|
|
654
|
+
},
|
|
655
|
+
body: JSON.stringify({ event: { type: "STORYBOOK_INDEX_CHANGED" } }),
|
|
656
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
657
|
+
});
|
|
658
|
+
if (!res.ok) {
|
|
659
|
+
console.warn("[STORYBOOK_PLUGIN] broadcast non-2xx", {
|
|
660
|
+
status: res.status,
|
|
661
|
+
statusText: res.statusText
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
} catch (err) {
|
|
665
|
+
console.warn(
|
|
666
|
+
"[STORYBOOK_PLUGIN] index-changed broadcast failed",
|
|
667
|
+
err instanceof Error ? err.message : String(err)
|
|
668
|
+
);
|
|
669
|
+
} finally {
|
|
670
|
+
inFlight = false;
|
|
671
|
+
if (pendingTrailingCall) {
|
|
672
|
+
pendingTrailingCall = false;
|
|
673
|
+
notifyOnbookIndexChanged();
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// src/utils/notifyOnbookIndexChanged/waitForIndexAndBroadcast.ts
|
|
679
|
+
var PROBE_INTERVAL_MS = 500;
|
|
680
|
+
var PROBE_CEILING_MS = 5 * 60 * 1e3;
|
|
681
|
+
async function waitForIndexAndBroadcast(port = 6006) {
|
|
682
|
+
const url = `http://localhost:${port}/onbook-index.json`;
|
|
683
|
+
const deadline = Date.now() + PROBE_CEILING_MS;
|
|
684
|
+
while (Date.now() < deadline) {
|
|
685
|
+
try {
|
|
686
|
+
const res = await fetch(url, {
|
|
687
|
+
signal: AbortSignal.timeout(2e3)
|
|
688
|
+
});
|
|
689
|
+
if (res.ok) {
|
|
690
|
+
notifyOnbookIndexChanged();
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
await new Promise((r) => setTimeout(r, PROBE_INTERVAL_MS));
|
|
696
|
+
}
|
|
697
|
+
console.warn("[STORYBOOK_PLUGIN] waitForIndexAndBroadcast hit ceiling without a 2xx", {
|
|
698
|
+
url,
|
|
699
|
+
ceilingMs: PROBE_CEILING_MS
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
|
|
323
703
|
// src/handlers/handleStoryFileChange/handleStoryFileChange.ts
|
|
324
704
|
var cachedIndex = null;
|
|
325
705
|
var indexFetchPromise = null;
|
|
326
706
|
var pendingFiles = /* @__PURE__ */ new Set();
|
|
327
707
|
var debounceTimer = null;
|
|
328
|
-
var
|
|
708
|
+
var DEBOUNCE_MS2 = 500;
|
|
329
709
|
async function fetchStorybookIndex() {
|
|
330
710
|
try {
|
|
331
711
|
const response = await fetch("http://localhost:6006/index.json");
|
|
@@ -339,7 +719,7 @@ async function fetchStorybookIndex() {
|
|
|
339
719
|
}
|
|
340
720
|
function getStoriesForFile(filePath) {
|
|
341
721
|
if (!cachedIndex) return [];
|
|
342
|
-
const fileName =
|
|
722
|
+
const fileName = path6.basename(filePath);
|
|
343
723
|
return Object.values(cachedIndex.entries).filter((entry) => entry.type === "story" && entry.importPath.endsWith(fileName)).map((entry) => entry.id);
|
|
344
724
|
}
|
|
345
725
|
async function regenerateScreenshotsForFiles(files) {
|
|
@@ -384,6 +764,7 @@ async function regenerateScreenshotsForFiles(files) {
|
|
|
384
764
|
function handleStoryFileChange({ file, modules }) {
|
|
385
765
|
if (file.endsWith(".stories.tsx") || file.endsWith(".stories.ts")) {
|
|
386
766
|
console.log(`[Screenshots] Story file changed: ${file}`);
|
|
767
|
+
notifyOnbookIndexChanged();
|
|
387
768
|
if (!cachedIndex && !indexFetchPromise) {
|
|
388
769
|
indexFetchPromise = fetchStorybookIndex();
|
|
389
770
|
}
|
|
@@ -400,10 +781,123 @@ function handleStoryFileChange({ file, modules }) {
|
|
|
400
781
|
} catch (error) {
|
|
401
782
|
console.error("[Screenshots] Error regenerating screenshots:", error);
|
|
402
783
|
}
|
|
403
|
-
},
|
|
784
|
+
}, DEBOUNCE_MS2);
|
|
404
785
|
return modules;
|
|
405
786
|
}
|
|
406
787
|
}
|
|
788
|
+
|
|
789
|
+
// src/screenshot-service/utils/console-logs/console-logs.ts
|
|
790
|
+
async function captureConsoleLogs(storyId, theme = "light", storybookUrl = "http://localhost:6006", timeoutMs = 3e4) {
|
|
791
|
+
const browser = await getBrowser();
|
|
792
|
+
const context = await browser.newContext({
|
|
793
|
+
viewport: { width: VIEWPORT_WIDTH, height: VIEWPORT_HEIGHT }
|
|
794
|
+
});
|
|
795
|
+
const page = await context.newPage();
|
|
796
|
+
const logs = [];
|
|
797
|
+
const pageErrors = [];
|
|
798
|
+
page.on("console", (msg) => {
|
|
799
|
+
const level = msg.type();
|
|
800
|
+
logs.push({
|
|
801
|
+
level: level === "warning" ? "warn" : level,
|
|
802
|
+
text: msg.text(),
|
|
803
|
+
timestamp: Date.now()
|
|
804
|
+
});
|
|
805
|
+
});
|
|
806
|
+
page.on("pageerror", (err) => {
|
|
807
|
+
pageErrors.push(err.message);
|
|
808
|
+
});
|
|
809
|
+
const url = `${storybookUrl}/iframe.html?id=${storyId}&viewMode=story&globals=theme:${theme}`;
|
|
810
|
+
try {
|
|
811
|
+
await page.goto(url, { timeout: timeoutMs });
|
|
812
|
+
await page.waitForLoadState("domcontentloaded", { timeout: timeoutMs });
|
|
813
|
+
await page.waitForLoadState("load", { timeout: timeoutMs });
|
|
814
|
+
try {
|
|
815
|
+
await page.waitForLoadState("networkidle", { timeout: 5e3 });
|
|
816
|
+
} catch {
|
|
817
|
+
}
|
|
818
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
819
|
+
return { logs, pageErrors, url };
|
|
820
|
+
} finally {
|
|
821
|
+
await context.close();
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// src/soft-story-rerender/soft-story-rerender.ts
|
|
826
|
+
function softStoryRerenderPlugin() {
|
|
827
|
+
let warnedOnce = false;
|
|
828
|
+
return {
|
|
829
|
+
name: "onbook-soft-story-rerender",
|
|
830
|
+
enforce: "post",
|
|
831
|
+
apply: "serve",
|
|
832
|
+
transform(code, _id) {
|
|
833
|
+
if (!code.includes("__STORYBOOK_PREVIEW__.onStoriesChanged")) return null;
|
|
834
|
+
const pattern = /import\.meta\.hot\.accept\((['"`])([^'"`\n]+)\1,\s*(?:async\s+)?\(?\s*newModule\s*\)?\s*=>\s*\{[\s\S]*?window\.__STORYBOOK_PREVIEW__\.onStoriesChanged\(\{\s*importFn:\s*newModule\.importFn\s*\}\);?\s*\}\);/;
|
|
835
|
+
if (!pattern.test(code)) {
|
|
836
|
+
if (!warnedOnce) {
|
|
837
|
+
warnedOnce = true;
|
|
838
|
+
console.warn(
|
|
839
|
+
"[onbook soft-story-rerender] preview bootstrap matched the file but the stories-accept-handler pattern did not \u2014 soft rerender disabled for this build of @storybook/builder-vite. Story edits will still work, just with the default Storybook flash."
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
const replacement = `import.meta.hot.accept($QUOTE$$PATH$$QUOTE$, async (newModule) => {
|
|
845
|
+
// ONL-1176 soft rerender \u2014 call rerender() on each active StoryRender
|
|
846
|
+
// instead of preview.onStoriesChanged() (which tears down + remounts).
|
|
847
|
+
// This accept handler fires in EVERY canvas iframe whenever ANY story
|
|
848
|
+
// file changes (every iframe's preview bundle imports the same virtual
|
|
849
|
+
// stories index). We identity-check each render's unboundStoryFn so
|
|
850
|
+
// iframes whose underlying story file didn't change stay perfectly
|
|
851
|
+
// still \u2014 no 1-frame no-op tick.
|
|
852
|
+
const preview = window.__STORYBOOK_PREVIEW__;
|
|
853
|
+
if (!preview || !preview.storyStoreValue) {
|
|
854
|
+
window.__STORYBOOK_PREVIEW__?.onStoriesChanged({ importFn: newModule.importFn });
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
try {
|
|
858
|
+
await preview.storyStoreValue.onStoriesChanged({ importFn: newModule.importFn });
|
|
859
|
+
const renders = preview.storyRenders || [];
|
|
860
|
+
if (renders.length === 0) {
|
|
861
|
+
preview.onStoriesChanged({ importFn: newModule.importFn });
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
await Promise.all(
|
|
865
|
+
renders.map(async (r) => {
|
|
866
|
+
try {
|
|
867
|
+
const nextStory = await preview.storyStoreValue.loadStory({ storyId: r.id });
|
|
868
|
+
if (!nextStory) return;
|
|
869
|
+
const prev = r.story;
|
|
870
|
+
const unchanged =
|
|
871
|
+
prev &&
|
|
872
|
+
prev.unboundStoryFn === nextStory.unboundStoryFn &&
|
|
873
|
+
prev.storyFn === nextStory.storyFn &&
|
|
874
|
+
prev.moduleExport === nextStory.moduleExport &&
|
|
875
|
+
prev.playFunction === nextStory.playFunction;
|
|
876
|
+
if (unchanged) return;
|
|
877
|
+
r.story = nextStory;
|
|
878
|
+
// Stories using the \`mount\` API can't be rerendered in place
|
|
879
|
+
// (their play function owns mounting), so fall back to remount.
|
|
880
|
+
if (nextStory.usesMount && r.remount) {
|
|
881
|
+
return r.remount();
|
|
882
|
+
}
|
|
883
|
+
return r.rerender();
|
|
884
|
+
} catch (innerErr) {
|
|
885
|
+
console.warn('[onbook] rerender failed for story', r.id, innerErr);
|
|
886
|
+
}
|
|
887
|
+
}),
|
|
888
|
+
);
|
|
889
|
+
} catch (err) {
|
|
890
|
+
console.warn('[onbook] soft rerender failed, falling back', err);
|
|
891
|
+
preview.onStoriesChanged({ importFn: newModule.importFn });
|
|
892
|
+
}
|
|
893
|
+
});`;
|
|
894
|
+
const out = code.replace(pattern, (_match, quote, path7) => {
|
|
895
|
+
return replacement.replace("$QUOTE$$PATH$$QUOTE$", `${quote}${path7}${quote}`);
|
|
896
|
+
});
|
|
897
|
+
return out === code ? null : { code: out, map: null };
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
}
|
|
407
901
|
function findGitRoot(startPath) {
|
|
408
902
|
let currentPath = startPath;
|
|
409
903
|
while (currentPath !== dirname(currentPath)) {
|
|
@@ -422,86 +916,71 @@ var gitRoot = findGitRoot(storybookDir);
|
|
|
422
916
|
var storybookLocation = gitRoot ? relative(gitRoot, storybookDir) : "";
|
|
423
917
|
var repoRoot = gitRoot || process.cwd();
|
|
424
918
|
var DEFAULT_ALLOWED_ORIGINS = [
|
|
425
|
-
"https://app.onlook.
|
|
919
|
+
"https://app.onlook.com",
|
|
426
920
|
"http://localhost:3000",
|
|
427
921
|
"http://localhost:6006"
|
|
428
922
|
];
|
|
429
|
-
var
|
|
430
|
-
var
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
if (importPath.includes(AUTO_STORIES_FOLDER)) {
|
|
443
|
-
autoStories.push(storyId);
|
|
444
|
-
} else {
|
|
445
|
-
userStories.push(storyId);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
const healthy = autoStories.filter((id) => !storyRuntimeErrors.has(id));
|
|
449
|
-
const broken = autoStories.filter((id) => storyRuntimeErrors.has(id)).map((id) => ({
|
|
450
|
-
storyId: id,
|
|
451
|
-
// biome-ignore lint/style/noNonNullAssertion: filtered above
|
|
452
|
-
error: storyRuntimeErrors.get(id)
|
|
453
|
-
}));
|
|
454
|
-
res.setHeader("Content-Type", "application/json");
|
|
455
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
456
|
-
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
457
|
-
res.end(
|
|
458
|
-
JSON.stringify({
|
|
459
|
-
autoStoryCount: autoStories.length,
|
|
460
|
-
userStoryCount: userStories.length,
|
|
461
|
-
healthy,
|
|
462
|
-
broken
|
|
463
|
-
})
|
|
464
|
-
);
|
|
465
|
-
}).catch((error) => {
|
|
466
|
-
res.statusCode = 500;
|
|
467
|
-
res.setHeader("Content-Type", "application/json");
|
|
468
|
-
res.end(
|
|
469
|
-
JSON.stringify({
|
|
470
|
-
error: "Failed to fetch story index",
|
|
471
|
-
details: String(error)
|
|
472
|
-
})
|
|
473
|
-
);
|
|
923
|
+
var manifestCache = null;
|
|
924
|
+
var manifestHash = null;
|
|
925
|
+
function readManifestFromDisk(filePath) {
|
|
926
|
+
try {
|
|
927
|
+
if (!fs4.existsSync(filePath)) return null;
|
|
928
|
+
const raw = fs4.readFileSync(filePath, "utf-8");
|
|
929
|
+
const hash = createHash("sha1").update(raw).digest("hex");
|
|
930
|
+
const manifest = JSON.parse(raw);
|
|
931
|
+
return { manifest, hash };
|
|
932
|
+
} catch (err) {
|
|
933
|
+
console.warn("[STORYBOOK_PLUGIN] Failed to read manifest", {
|
|
934
|
+
filePath,
|
|
935
|
+
error: err instanceof Error ? err.message : String(err)
|
|
474
936
|
});
|
|
475
|
-
return;
|
|
937
|
+
return null;
|
|
476
938
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
939
|
+
}
|
|
940
|
+
function refreshManifest(filePath) {
|
|
941
|
+
const result = readManifestFromDisk(filePath);
|
|
942
|
+
if (!result) {
|
|
943
|
+
return false;
|
|
944
|
+
}
|
|
945
|
+
if (result.hash === manifestHash) return false;
|
|
946
|
+
manifestCache = result.manifest;
|
|
947
|
+
manifestHash = result.hash;
|
|
948
|
+
console.log("[STORYBOOK_PLUGIN] Manifest cache refreshed", {
|
|
949
|
+
filePath,
|
|
950
|
+
hash: result.hash.slice(0, 8),
|
|
951
|
+
storyCount: Object.keys(result.manifest.stories ?? {}).length
|
|
952
|
+
});
|
|
953
|
+
return true;
|
|
954
|
+
}
|
|
955
|
+
var TAILWIND_ENTRY_CANDIDATES = [
|
|
956
|
+
"src/app/globals.css",
|
|
957
|
+
"app/globals.css",
|
|
958
|
+
"src/index.css",
|
|
959
|
+
"src/main.css",
|
|
960
|
+
"src/styles.css",
|
|
961
|
+
"src/styles/globals.css",
|
|
962
|
+
"src/styles/index.css",
|
|
963
|
+
"styles/globals.css",
|
|
964
|
+
"styles/index.css",
|
|
965
|
+
"app.css",
|
|
966
|
+
"index.css"
|
|
967
|
+
];
|
|
968
|
+
var TAILWIND_DIRECTIVE_PATTERN = /@import\s+['"](?:tailwindcss|tailwindcss\/.+)['"]|@tailwind\s+(?:base|components|utilities)/;
|
|
969
|
+
function detectTailwindEntryCss(cwd) {
|
|
970
|
+
for (const rel of TAILWIND_ENTRY_CANDIDATES) {
|
|
971
|
+
const abs = path6.join(cwd, rel);
|
|
972
|
+
try {
|
|
973
|
+
if (!fs4.existsSync(abs)) continue;
|
|
974
|
+
const head = fs4.readFileSync(abs, "utf-8").slice(0, 2048);
|
|
975
|
+
if (TAILWIND_DIRECTIVE_PATTERN.test(head)) return abs;
|
|
976
|
+
} catch {
|
|
977
|
+
}
|
|
501
978
|
}
|
|
979
|
+
return null;
|
|
980
|
+
}
|
|
981
|
+
var serveMetadataAndScreenshots = (req, res, next) => {
|
|
502
982
|
if (req.url === "/onbook-index.json") {
|
|
503
983
|
console.log("[STORYBOOK_PLUGIN] Serving /onbook-index.json endpoint");
|
|
504
|
-
const manifestPath = path.join(process.cwd(), ".storybook-cache", "manifest.json");
|
|
505
984
|
const cacheBuster = Date.now();
|
|
506
985
|
console.log("[STORYBOOK_PLUGIN] Fetching http://localhost:6006/index.json");
|
|
507
986
|
fetch(`http://localhost:6006/index.json?_t=${cacheBuster}`, {
|
|
@@ -518,7 +997,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
518
997
|
});
|
|
519
998
|
return response.json();
|
|
520
999
|
}).then((indexData) => {
|
|
521
|
-
const manifest =
|
|
1000
|
+
const manifest = manifestCache ?? { stories: {} };
|
|
522
1001
|
const defaultBoundingBox = { width: 1920, height: 1080 };
|
|
523
1002
|
for (const [storyId, entry] of Object.entries(indexData.entries || {})) {
|
|
524
1003
|
const manifestEntry = manifest.stories?.[storyId];
|
|
@@ -585,8 +1064,42 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
585
1064
|
});
|
|
586
1065
|
return;
|
|
587
1066
|
}
|
|
1067
|
+
if (req.url?.startsWith("/api/console-logs")) {
|
|
1068
|
+
const url = new URL(req.url, "http://localhost");
|
|
1069
|
+
const storyId = url.searchParams.get("storyId");
|
|
1070
|
+
const theme = url.searchParams.get("theme") || "light";
|
|
1071
|
+
if (!storyId) {
|
|
1072
|
+
res.statusCode = 400;
|
|
1073
|
+
res.setHeader("Content-Type", "application/json");
|
|
1074
|
+
res.end(JSON.stringify({ error: "storyId is required" }));
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
if (theme !== "light" && theme !== "dark") {
|
|
1078
|
+
res.statusCode = 400;
|
|
1079
|
+
res.setHeader("Content-Type", "application/json");
|
|
1080
|
+
res.end(JSON.stringify({ error: 'theme must be "light" or "dark"' }));
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
captureConsoleLogs(storyId, theme).then((result) => {
|
|
1084
|
+
res.setHeader("Content-Type", "application/json");
|
|
1085
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1086
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
1087
|
+
res.end(JSON.stringify(result));
|
|
1088
|
+
}).catch((error) => {
|
|
1089
|
+
console.error("Console log capture error:", error);
|
|
1090
|
+
res.statusCode = 500;
|
|
1091
|
+
res.setHeader("Content-Type", "application/json");
|
|
1092
|
+
res.end(
|
|
1093
|
+
JSON.stringify({
|
|
1094
|
+
error: "Failed to capture console logs",
|
|
1095
|
+
details: String(error)
|
|
1096
|
+
})
|
|
1097
|
+
);
|
|
1098
|
+
});
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
588
1101
|
if (req.url?.startsWith("/screenshots/")) {
|
|
589
|
-
const screenshotPath =
|
|
1102
|
+
const screenshotPath = path6.join(
|
|
590
1103
|
process.cwd(),
|
|
591
1104
|
".storybook-cache",
|
|
592
1105
|
req.url.replace("/screenshots/", "screenshots/")
|
|
@@ -595,11 +1108,11 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
595
1108
|
const storyId = urlParts[0];
|
|
596
1109
|
const themeFile = urlParts[1];
|
|
597
1110
|
const theme = themeFile?.replace(".png", "");
|
|
598
|
-
if (
|
|
1111
|
+
if (fs4.existsSync(screenshotPath)) {
|
|
599
1112
|
res.setHeader("Content-Type", "image/png");
|
|
600
1113
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
601
1114
|
res.setHeader("Cache-Control", "public, max-age=3600");
|
|
602
|
-
|
|
1115
|
+
fs4.createReadStream(screenshotPath).pipe(res);
|
|
603
1116
|
return;
|
|
604
1117
|
}
|
|
605
1118
|
if (storyId && theme && (theme === "light" || theme === "dark")) {
|
|
@@ -607,16 +1120,16 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
607
1120
|
`[STORYBOOK_PLUGIN] Generating screenshot on-demand: ${storyId}/${theme}`
|
|
608
1121
|
);
|
|
609
1122
|
captureScreenshotBuffer(storyId, theme).then(({ buffer }) => {
|
|
610
|
-
const storyDir =
|
|
1123
|
+
const storyDir = path6.join(
|
|
611
1124
|
process.cwd(),
|
|
612
1125
|
".storybook-cache",
|
|
613
1126
|
"screenshots",
|
|
614
1127
|
storyId
|
|
615
1128
|
);
|
|
616
|
-
if (!
|
|
617
|
-
|
|
1129
|
+
if (!fs4.existsSync(storyDir)) {
|
|
1130
|
+
fs4.mkdirSync(storyDir, { recursive: true });
|
|
618
1131
|
}
|
|
619
|
-
|
|
1132
|
+
fs4.writeFileSync(screenshotPath, buffer);
|
|
620
1133
|
res.setHeader("Content-Type", "image/png");
|
|
621
1134
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
622
1135
|
res.setHeader("Cache-Control", "public, max-age=3600");
|
|
@@ -628,6 +1141,8 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
628
1141
|
);
|
|
629
1142
|
res.statusCode = 500;
|
|
630
1143
|
res.setHeader("Content-Type", "application/json");
|
|
1144
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1145
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
631
1146
|
res.end(
|
|
632
1147
|
JSON.stringify({
|
|
633
1148
|
error: "Failed to generate screenshot",
|
|
@@ -638,11 +1153,41 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
638
1153
|
return;
|
|
639
1154
|
}
|
|
640
1155
|
res.statusCode = 404;
|
|
1156
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1157
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
641
1158
|
res.end("Screenshot not found");
|
|
642
1159
|
return;
|
|
643
1160
|
}
|
|
644
1161
|
next();
|
|
645
1162
|
};
|
|
1163
|
+
var ONLOOK_PLUGIN_CLAIMS_GLOBAL = "__ONLOOK_STORYBOOK_PLUGIN_CLAIMS__";
|
|
1164
|
+
function claimRegistry() {
|
|
1165
|
+
const g = globalThis;
|
|
1166
|
+
let registry = g[ONLOOK_PLUGIN_CLAIMS_GLOBAL];
|
|
1167
|
+
if (!registry) {
|
|
1168
|
+
registry = /* @__PURE__ */ new WeakMap();
|
|
1169
|
+
g[ONLOOK_PLUGIN_CLAIMS_GLOBAL] = registry;
|
|
1170
|
+
}
|
|
1171
|
+
return registry;
|
|
1172
|
+
}
|
|
1173
|
+
function withDedupeApply(plugins, instanceId) {
|
|
1174
|
+
for (const plugin of plugins) {
|
|
1175
|
+
const prior = plugin.apply;
|
|
1176
|
+
plugin.apply = (config, env) => {
|
|
1177
|
+
const priorOk = prior === void 0 ? true : typeof prior === "string" ? env.command === prior : prior(config, env);
|
|
1178
|
+
if (!priorOk) return false;
|
|
1179
|
+
if (env === null || typeof env !== "object") return true;
|
|
1180
|
+
const registry = claimRegistry();
|
|
1181
|
+
const claimed = registry.get(env);
|
|
1182
|
+
if (claimed === void 0) {
|
|
1183
|
+
registry.set(env, instanceId);
|
|
1184
|
+
return true;
|
|
1185
|
+
}
|
|
1186
|
+
return claimed === instanceId;
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
return plugins;
|
|
1190
|
+
}
|
|
646
1191
|
function storybookOnlookPlugin(options = {}) {
|
|
647
1192
|
if (process.env.CHROMATIC || process.env.CI) {
|
|
648
1193
|
console.log("[STORYBOOK_PLUGIN] Disabled in CI/Chromatic environment");
|
|
@@ -650,25 +1195,28 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
650
1195
|
}
|
|
651
1196
|
const port = options.port ?? 6006;
|
|
652
1197
|
const allowedOrigins = [...DEFAULT_ALLOWED_ORIGINS, ...options.allowedOrigins ?? []];
|
|
1198
|
+
const hmrOverride = options.hmr;
|
|
653
1199
|
console.log("[STORYBOOK_PLUGIN] Plugin initialized", {
|
|
654
1200
|
port,
|
|
655
1201
|
allowedOrigins,
|
|
656
1202
|
storybookLocation,
|
|
657
|
-
repoRoot
|
|
1203
|
+
repoRoot,
|
|
1204
|
+
hmr: hmrOverride ?? null
|
|
658
1205
|
});
|
|
659
1206
|
const mainPlugin = {
|
|
660
1207
|
name: "storybook-onlook-plugin",
|
|
661
1208
|
config() {
|
|
662
1209
|
return {
|
|
663
1210
|
server: {
|
|
664
|
-
//
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
1211
|
+
// HMR override applies only when the caller explicitly opts in
|
|
1212
|
+
// (typically: Storybook fronted over HTTPS by a reverse proxy).
|
|
1213
|
+
// Locally, omitting this lets Vite use defaults
|
|
1214
|
+
// (ws://localhost:<port>) and HMR works as expected.
|
|
1215
|
+
...hmrOverride && {
|
|
1216
|
+
hmr: {
|
|
1217
|
+
...hmrOverride,
|
|
1218
|
+
port
|
|
1219
|
+
}
|
|
672
1220
|
},
|
|
673
1221
|
cors: {
|
|
674
1222
|
origin: allowedOrigins
|
|
@@ -679,60 +1227,112 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
679
1227
|
configureServer(server) {
|
|
680
1228
|
console.log("[STORYBOOK_PLUGIN] Configuring server middleware");
|
|
681
1229
|
server.middlewares.use(serveMetadataAndScreenshots);
|
|
1230
|
+
const cachedManifestPath = path6.join(
|
|
1231
|
+
process.cwd(),
|
|
1232
|
+
".storybook-cache",
|
|
1233
|
+
"manifest.json"
|
|
1234
|
+
);
|
|
1235
|
+
refreshManifest(cachedManifestPath);
|
|
1236
|
+
let pending2;
|
|
1237
|
+
const scheduleRefresh = (file) => {
|
|
1238
|
+
if (file !== cachedManifestPath) return;
|
|
1239
|
+
if (pending2) clearTimeout(pending2);
|
|
1240
|
+
pending2 = setTimeout(() => {
|
|
1241
|
+
pending2 = void 0;
|
|
1242
|
+
refreshManifest(cachedManifestPath);
|
|
1243
|
+
}, 100);
|
|
1244
|
+
};
|
|
1245
|
+
server.watcher.on("add", scheduleRefresh);
|
|
1246
|
+
server.watcher.on("change", scheduleRefresh);
|
|
1247
|
+
const storyGlobs = options.storyGlobs ?? ["**/*.stories.@(js|jsx|mjs|ts|tsx|mdx)"];
|
|
1248
|
+
const resolvedStoryGlobs = storyGlobs.map(
|
|
1249
|
+
(glob) => path6.isAbsolute(glob) ? glob : path6.join(process.cwd(), glob)
|
|
1250
|
+
);
|
|
1251
|
+
if (resolvedStoryGlobs.length > 0) {
|
|
1252
|
+
server.watcher.add(resolvedStoryGlobs);
|
|
1253
|
+
console.log("[STORYBOOK_PLUGIN] Extended watcher coverage", {
|
|
1254
|
+
storyGlobs: resolvedStoryGlobs
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
const tailwindEntryPath = (() => {
|
|
1258
|
+
if (options.tailwindEntryCss === false) return null;
|
|
1259
|
+
if (typeof options.tailwindEntryCss === "string") {
|
|
1260
|
+
return path6.isAbsolute(options.tailwindEntryCss) ? options.tailwindEntryCss : path6.join(process.cwd(), options.tailwindEntryCss);
|
|
1261
|
+
}
|
|
1262
|
+
return detectTailwindEntryCss(process.cwd());
|
|
1263
|
+
})();
|
|
1264
|
+
if (tailwindEntryPath) {
|
|
1265
|
+
console.log("[STORYBOOK_PLUGIN] Tailwind rescan watcher enabled", {
|
|
1266
|
+
entry: tailwindEntryPath,
|
|
1267
|
+
autoDetected: options.tailwindEntryCss === void 0
|
|
1268
|
+
});
|
|
1269
|
+
const seenDirs = /* @__PURE__ */ new Set();
|
|
1270
|
+
let watcherReady = false;
|
|
1271
|
+
server.watcher.once("ready", () => {
|
|
1272
|
+
watcherReady = true;
|
|
1273
|
+
});
|
|
1274
|
+
server.watcher.on("add", (file) => {
|
|
1275
|
+
if (!/\.tsx?$/.test(file)) return;
|
|
1276
|
+
const dir = path6.dirname(file);
|
|
1277
|
+
const isNewDir = !seenDirs.has(dir);
|
|
1278
|
+
seenDirs.add(dir);
|
|
1279
|
+
if (!watcherReady || !isNewDir) return;
|
|
1280
|
+
try {
|
|
1281
|
+
const now = /* @__PURE__ */ new Date();
|
|
1282
|
+
fs4.utimesSync(tailwindEntryPath, now, now);
|
|
1283
|
+
console.log("[STORYBOOK_PLUGIN] Tailwind rescan for new dir", {
|
|
1284
|
+
dir,
|
|
1285
|
+
entry: tailwindEntryPath
|
|
1286
|
+
});
|
|
1287
|
+
} catch (err) {
|
|
1288
|
+
console.warn("[STORYBOOK_PLUGIN] Tailwind rescan: utimes failed", {
|
|
1289
|
+
entry: tailwindEntryPath,
|
|
1290
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
682
1295
|
server.httpServer?.once("listening", () => {
|
|
1296
|
+
const probePort = server.config.server.port ?? port;
|
|
683
1297
|
console.log("[STORYBOOK_PLUGIN] Server is listening", {
|
|
684
|
-
port:
|
|
1298
|
+
port: probePort,
|
|
685
1299
|
host: server.config.server.host
|
|
686
1300
|
});
|
|
1301
|
+
void waitForIndexAndBroadcast(probePort);
|
|
687
1302
|
});
|
|
688
1303
|
},
|
|
689
1304
|
configurePreviewServer(server) {
|
|
690
1305
|
console.log("[STORYBOOK_PLUGIN] Configuring preview server middleware");
|
|
691
1306
|
server.middlewares.use(serveMetadataAndScreenshots);
|
|
1307
|
+
refreshManifest(path6.join(process.cwd(), ".storybook-cache", "manifest.json"));
|
|
692
1308
|
},
|
|
693
|
-
handleHotUpdate
|
|
694
|
-
if (ctx.file.includes(AUTO_STORIES_FOLDER) && ctx.file.endsWith(".stories.tsx")) {
|
|
695
|
-
enrichStoryFile(ctx.file);
|
|
696
|
-
}
|
|
697
|
-
return handleStoryFileChange(ctx);
|
|
698
|
-
}
|
|
1309
|
+
handleHotUpdate: handleStoryFileChange
|
|
699
1310
|
};
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
});
|
|
1311
|
+
const envContainment = envContainmentPlugin(options.envContainment);
|
|
1312
|
+
const moduleLoadCatchAll = options.moduleLoadCatchAll === false ? null : moduleLoadCatchAllPlugin();
|
|
1313
|
+
return withDedupeApply(
|
|
1314
|
+
[
|
|
1315
|
+
...envContainment ? [envContainment] : [],
|
|
1316
|
+
...moduleLoadCatchAll ? [moduleLoadCatchAll] : [],
|
|
1317
|
+
componentLocPlugin(),
|
|
1318
|
+
mainPlugin,
|
|
1319
|
+
fastRefreshTolerantExportsPlugin(),
|
|
1320
|
+
softStoryRerenderPlugin()
|
|
1321
|
+
],
|
|
1322
|
+
/* @__PURE__ */ Symbol("onlook-storybook-plugin-instance")
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
var tolerantCsfIndexer = {
|
|
1326
|
+
test: /(stories|story)\.(m?js|ts)x?$/,
|
|
1327
|
+
createIndex: async (fileName, options) => {
|
|
718
1328
|
try {
|
|
719
|
-
|
|
720
|
-
autoStoryGenerator.vite({
|
|
721
|
-
preset: "react",
|
|
722
|
-
imports,
|
|
723
|
-
ignores,
|
|
724
|
-
storiesFolder: AUTO_STORIES_FOLDER,
|
|
725
|
-
isGenerateStoriesFileAtBuild: true
|
|
726
|
-
})
|
|
727
|
-
);
|
|
1329
|
+
return (await readCsf(fileName, options)).parse().indexInputs;
|
|
728
1330
|
} catch (err) {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
);
|
|
1331
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1332
|
+
console.warn("[onbook] Skipping broken story file:", fileName, msg);
|
|
1333
|
+
return [];
|
|
733
1334
|
}
|
|
734
1335
|
}
|
|
735
|
-
|
|
736
|
-
}
|
|
1336
|
+
};
|
|
737
1337
|
|
|
738
|
-
export {
|
|
1338
|
+
export { MODULE_LOAD_CATCH_ALL_PLUGIN_NAME, ONLOOK_CONTAINED_CARD_ATTR, ONLOOK_CONTAINED_EVENT, ONLOOK_CONTAINED_MODULES_GLOBAL, ONLOOK_CONTAINED_PROP, ONLOOK_CONTAINMENT_CONTRACT_GLOBAL, ONLOOK_CONTAINMENT_CONTRACT_VERSION, ONLOOK_RENDERED_EVENT, ONLOOK_RENDER_STATE_ATTR, ONLOOK_RENDER_STORY_ATTR, ONLOOK_UNCONTAINED_ERROR_EVENT, RESOLVED_STORYBOOK_VIRTUAL_STORIES_ID, STORYBOOK_VIRTUAL_STORIES_ID, envContainmentPlugin, moduleLoadCatchAllPlugin, storybookOnlookPlugin, tolerantCsfIndexer };
|