@onlook/storybook-plugin 0.4.0-beta.1 → 0.4.0-beta.11
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 +929 -217
- package/dist/preset/index.d.ts +10 -0
- package/dist/preset/index.js +1458 -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-B9Eo_OIq.d.ts +62 -0
- package/package.json +19 -9
package/dist/index.js
CHANGED
|
@@ -1,97 +1,309 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import path7, { dirname, join, relative } from 'path';
|
|
3
|
+
import crypto, { createHash } from 'crypto';
|
|
1
4
|
import fs5, { existsSync } from 'fs';
|
|
2
|
-
import path, { dirname, join, relative } from 'path';
|
|
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(path7.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 (path7.isAbsolute(cleaned)) {
|
|
100
|
+
direct = cleaned;
|
|
101
|
+
} else if (importer && (cleaned.startsWith("./") || cleaned.startsWith("../"))) {
|
|
102
|
+
direct = path7.resolve(path7.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 ? path7.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
|
-
} catch (err) {
|
|
64
|
-
console.error(`[AutoStories] Failed to parse component: ${componentPath}`, err);
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
131
|
+
};
|
|
67
132
|
}
|
|
68
|
-
function
|
|
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");
|
|
191
|
+
}
|
|
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;
|
|
94
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;
|
|
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 = path7.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 = path7.join(process.cwd(), ".storybook-cache");
|
|
391
|
+
var SCREENSHOTS_DIR = path7.join(CACHE_DIR, "screenshots");
|
|
392
|
+
var MANIFEST_PATH = path7.join(CACHE_DIR, "manifest.json");
|
|
158
393
|
var VIEWPORT_WIDTH = 1920;
|
|
159
394
|
var VIEWPORT_HEIGHT = 1080;
|
|
160
395
|
var MIN_COMPONENT_WIDTH = 420;
|
|
@@ -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 = path7.join(SCREENSHOTS_DIR, storyId);
|
|
514
|
+
return path7.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,7 +598,7 @@ 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 =
|
|
601
|
+
const storyDir = path7.join(SCREENSHOTS_DIR, storyId);
|
|
303
602
|
if (!fs5.existsSync(storyDir)) {
|
|
304
603
|
fs5.mkdirSync(storyDir, { recursive: true });
|
|
305
604
|
}
|
|
@@ -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 = path7.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,225 @@ 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, path8) => {
|
|
895
|
+
return replacement.replace("$QUOTE$$PATH$$QUOTE$", `${quote}${path8}${quote}`);
|
|
896
|
+
});
|
|
897
|
+
return out === code ? null : { code: out, map: null };
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
var TSCONFIG_CANDIDATES = ["tsconfig.json", "tsconfig.app.json", "tsconfig.base.json"];
|
|
902
|
+
function resolveTsconfigAliases(root) {
|
|
903
|
+
try {
|
|
904
|
+
const config = readTsconfigAliasConfig(root);
|
|
905
|
+
if (!config) return [];
|
|
906
|
+
const base = path7.resolve(root, config.baseUrl ?? ".");
|
|
907
|
+
const aliases = [];
|
|
908
|
+
for (const [key, targets] of Object.entries(config.paths)) {
|
|
909
|
+
const target = targets[0];
|
|
910
|
+
if (!target) continue;
|
|
911
|
+
if (key.endsWith("/*") && target.endsWith("/*")) {
|
|
912
|
+
const findPrefix = key.slice(0, -1);
|
|
913
|
+
const targetDir = path7.resolve(base, target.slice(0, -2));
|
|
914
|
+
aliases.push({
|
|
915
|
+
find: new RegExp(`^${escapeRegExp(findPrefix)}`),
|
|
916
|
+
replacement: `${targetDir}/`
|
|
917
|
+
});
|
|
918
|
+
} else if (!key.includes("*") && !target.includes("*")) {
|
|
919
|
+
aliases.push({
|
|
920
|
+
find: new RegExp(`^${escapeRegExp(key)}$`),
|
|
921
|
+
replacement: path7.resolve(base, target)
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
return aliases.sort((a, b) => b.find.source.length - a.find.source.length);
|
|
926
|
+
} catch {
|
|
927
|
+
return [];
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
function readTsconfigAliasConfig(root) {
|
|
931
|
+
for (const candidate of TSCONFIG_CANDIDATES) {
|
|
932
|
+
const abs = path7.join(root, candidate);
|
|
933
|
+
let text;
|
|
934
|
+
try {
|
|
935
|
+
text = fs5.readFileSync(abs, "utf-8");
|
|
936
|
+
} catch {
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
let parsed;
|
|
940
|
+
try {
|
|
941
|
+
parsed = JSON.parse(stripJsonComments(text));
|
|
942
|
+
} catch {
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
const paths = parsed.compilerOptions?.paths;
|
|
946
|
+
if (paths && typeof paths === "object" && Object.keys(paths).length > 0) {
|
|
947
|
+
const baseUrl = parsed.compilerOptions?.baseUrl;
|
|
948
|
+
return typeof baseUrl === "string" ? { baseUrl, paths } : { paths };
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
return null;
|
|
952
|
+
}
|
|
953
|
+
function escapeRegExp(value) {
|
|
954
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
955
|
+
}
|
|
956
|
+
function stripJsonComments(text) {
|
|
957
|
+
let out = "";
|
|
958
|
+
let inString = false;
|
|
959
|
+
let inLine = false;
|
|
960
|
+
let inBlock = false;
|
|
961
|
+
for (let i = 0; i < text.length; i++) {
|
|
962
|
+
const c = text.charAt(i);
|
|
963
|
+
const next = text.charAt(i + 1);
|
|
964
|
+
if (inLine) {
|
|
965
|
+
if (c === "\n") {
|
|
966
|
+
inLine = false;
|
|
967
|
+
out += c;
|
|
968
|
+
}
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
if (inBlock) {
|
|
972
|
+
if (c === "*" && next === "/") {
|
|
973
|
+
inBlock = false;
|
|
974
|
+
i++;
|
|
975
|
+
}
|
|
976
|
+
continue;
|
|
977
|
+
}
|
|
978
|
+
if (inString) {
|
|
979
|
+
out += c;
|
|
980
|
+
if (c === "\\") {
|
|
981
|
+
out += next;
|
|
982
|
+
i++;
|
|
983
|
+
} else if (c === '"') {
|
|
984
|
+
inString = false;
|
|
985
|
+
}
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
if (c === '"') {
|
|
989
|
+
inString = true;
|
|
990
|
+
out += c;
|
|
991
|
+
} else if (c === "/" && next === "/") {
|
|
992
|
+
inLine = true;
|
|
993
|
+
i++;
|
|
994
|
+
} else if (c === "/" && next === "*") {
|
|
995
|
+
inBlock = true;
|
|
996
|
+
i++;
|
|
997
|
+
} else {
|
|
998
|
+
out += c;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return out;
|
|
1002
|
+
}
|
|
407
1003
|
function findGitRoot(startPath) {
|
|
408
1004
|
let currentPath = startPath;
|
|
409
1005
|
while (currentPath !== dirname(currentPath)) {
|
|
@@ -422,86 +1018,71 @@ var gitRoot = findGitRoot(storybookDir);
|
|
|
422
1018
|
var storybookLocation = gitRoot ? relative(gitRoot, storybookDir) : "";
|
|
423
1019
|
var repoRoot = gitRoot || process.cwd();
|
|
424
1020
|
var DEFAULT_ALLOWED_ORIGINS = [
|
|
425
|
-
"https://app.onlook.
|
|
1021
|
+
"https://app.onlook.com",
|
|
426
1022
|
"http://localhost:3000",
|
|
427
1023
|
"http://localhost:6006"
|
|
428
1024
|
];
|
|
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
|
-
);
|
|
1025
|
+
var manifestCache = null;
|
|
1026
|
+
var manifestHash = null;
|
|
1027
|
+
function readManifestFromDisk(filePath) {
|
|
1028
|
+
try {
|
|
1029
|
+
if (!fs5.existsSync(filePath)) return null;
|
|
1030
|
+
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
1031
|
+
const hash = createHash("sha1").update(raw).digest("hex");
|
|
1032
|
+
const manifest = JSON.parse(raw);
|
|
1033
|
+
return { manifest, hash };
|
|
1034
|
+
} catch (err) {
|
|
1035
|
+
console.warn("[STORYBOOK_PLUGIN] Failed to read manifest", {
|
|
1036
|
+
filePath,
|
|
1037
|
+
error: err instanceof Error ? err.message : String(err)
|
|
474
1038
|
});
|
|
475
|
-
return;
|
|
1039
|
+
return null;
|
|
476
1040
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
1041
|
+
}
|
|
1042
|
+
function refreshManifest(filePath) {
|
|
1043
|
+
const result = readManifestFromDisk(filePath);
|
|
1044
|
+
if (!result) {
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
if (result.hash === manifestHash) return false;
|
|
1048
|
+
manifestCache = result.manifest;
|
|
1049
|
+
manifestHash = result.hash;
|
|
1050
|
+
console.log("[STORYBOOK_PLUGIN] Manifest cache refreshed", {
|
|
1051
|
+
filePath,
|
|
1052
|
+
hash: result.hash.slice(0, 8),
|
|
1053
|
+
storyCount: Object.keys(result.manifest.stories ?? {}).length
|
|
1054
|
+
});
|
|
1055
|
+
return true;
|
|
1056
|
+
}
|
|
1057
|
+
var TAILWIND_ENTRY_CANDIDATES = [
|
|
1058
|
+
"src/app/globals.css",
|
|
1059
|
+
"app/globals.css",
|
|
1060
|
+
"src/index.css",
|
|
1061
|
+
"src/main.css",
|
|
1062
|
+
"src/styles.css",
|
|
1063
|
+
"src/styles/globals.css",
|
|
1064
|
+
"src/styles/index.css",
|
|
1065
|
+
"styles/globals.css",
|
|
1066
|
+
"styles/index.css",
|
|
1067
|
+
"app.css",
|
|
1068
|
+
"index.css"
|
|
1069
|
+
];
|
|
1070
|
+
var TAILWIND_DIRECTIVE_PATTERN = /@import\s+['"](?:tailwindcss|tailwindcss\/.+)['"]|@tailwind\s+(?:base|components|utilities)/;
|
|
1071
|
+
function detectTailwindEntryCss(cwd) {
|
|
1072
|
+
for (const rel of TAILWIND_ENTRY_CANDIDATES) {
|
|
1073
|
+
const abs = path7.join(cwd, rel);
|
|
1074
|
+
try {
|
|
1075
|
+
if (!fs5.existsSync(abs)) continue;
|
|
1076
|
+
const head = fs5.readFileSync(abs, "utf-8").slice(0, 2048);
|
|
1077
|
+
if (TAILWIND_DIRECTIVE_PATTERN.test(head)) return abs;
|
|
1078
|
+
} catch {
|
|
1079
|
+
}
|
|
501
1080
|
}
|
|
1081
|
+
return null;
|
|
1082
|
+
}
|
|
1083
|
+
var serveMetadataAndScreenshots = (req, res, next) => {
|
|
502
1084
|
if (req.url === "/onbook-index.json") {
|
|
503
1085
|
console.log("[STORYBOOK_PLUGIN] Serving /onbook-index.json endpoint");
|
|
504
|
-
const manifestPath = path.join(process.cwd(), ".storybook-cache", "manifest.json");
|
|
505
1086
|
const cacheBuster = Date.now();
|
|
506
1087
|
console.log("[STORYBOOK_PLUGIN] Fetching http://localhost:6006/index.json");
|
|
507
1088
|
fetch(`http://localhost:6006/index.json?_t=${cacheBuster}`, {
|
|
@@ -518,7 +1099,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
518
1099
|
});
|
|
519
1100
|
return response.json();
|
|
520
1101
|
}).then((indexData) => {
|
|
521
|
-
const manifest =
|
|
1102
|
+
const manifest = manifestCache ?? { stories: {} };
|
|
522
1103
|
const defaultBoundingBox = { width: 1920, height: 1080 };
|
|
523
1104
|
for (const [storyId, entry] of Object.entries(indexData.entries || {})) {
|
|
524
1105
|
const manifestEntry = manifest.stories?.[storyId];
|
|
@@ -585,8 +1166,42 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
585
1166
|
});
|
|
586
1167
|
return;
|
|
587
1168
|
}
|
|
1169
|
+
if (req.url?.startsWith("/api/console-logs")) {
|
|
1170
|
+
const url = new URL(req.url, "http://localhost");
|
|
1171
|
+
const storyId = url.searchParams.get("storyId");
|
|
1172
|
+
const theme = url.searchParams.get("theme") || "light";
|
|
1173
|
+
if (!storyId) {
|
|
1174
|
+
res.statusCode = 400;
|
|
1175
|
+
res.setHeader("Content-Type", "application/json");
|
|
1176
|
+
res.end(JSON.stringify({ error: "storyId is required" }));
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
if (theme !== "light" && theme !== "dark") {
|
|
1180
|
+
res.statusCode = 400;
|
|
1181
|
+
res.setHeader("Content-Type", "application/json");
|
|
1182
|
+
res.end(JSON.stringify({ error: 'theme must be "light" or "dark"' }));
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
captureConsoleLogs(storyId, theme).then((result) => {
|
|
1186
|
+
res.setHeader("Content-Type", "application/json");
|
|
1187
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1188
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
1189
|
+
res.end(JSON.stringify(result));
|
|
1190
|
+
}).catch((error) => {
|
|
1191
|
+
console.error("Console log capture error:", error);
|
|
1192
|
+
res.statusCode = 500;
|
|
1193
|
+
res.setHeader("Content-Type", "application/json");
|
|
1194
|
+
res.end(
|
|
1195
|
+
JSON.stringify({
|
|
1196
|
+
error: "Failed to capture console logs",
|
|
1197
|
+
details: String(error)
|
|
1198
|
+
})
|
|
1199
|
+
);
|
|
1200
|
+
});
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
588
1203
|
if (req.url?.startsWith("/screenshots/")) {
|
|
589
|
-
const screenshotPath =
|
|
1204
|
+
const screenshotPath = path7.join(
|
|
590
1205
|
process.cwd(),
|
|
591
1206
|
".storybook-cache",
|
|
592
1207
|
req.url.replace("/screenshots/", "screenshots/")
|
|
@@ -607,7 +1222,7 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
607
1222
|
`[STORYBOOK_PLUGIN] Generating screenshot on-demand: ${storyId}/${theme}`
|
|
608
1223
|
);
|
|
609
1224
|
captureScreenshotBuffer(storyId, theme).then(({ buffer }) => {
|
|
610
|
-
const storyDir =
|
|
1225
|
+
const storyDir = path7.join(
|
|
611
1226
|
process.cwd(),
|
|
612
1227
|
".storybook-cache",
|
|
613
1228
|
"screenshots",
|
|
@@ -628,6 +1243,8 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
628
1243
|
);
|
|
629
1244
|
res.statusCode = 500;
|
|
630
1245
|
res.setHeader("Content-Type", "application/json");
|
|
1246
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1247
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
631
1248
|
res.end(
|
|
632
1249
|
JSON.stringify({
|
|
633
1250
|
error: "Failed to generate screenshot",
|
|
@@ -638,11 +1255,41 @@ var serveMetadataAndScreenshots = (req, res, next) => {
|
|
|
638
1255
|
return;
|
|
639
1256
|
}
|
|
640
1257
|
res.statusCode = 404;
|
|
1258
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
1259
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
641
1260
|
res.end("Screenshot not found");
|
|
642
1261
|
return;
|
|
643
1262
|
}
|
|
644
1263
|
next();
|
|
645
1264
|
};
|
|
1265
|
+
var ONLOOK_PLUGIN_CLAIMS_GLOBAL = "__ONLOOK_STORYBOOK_PLUGIN_CLAIMS__";
|
|
1266
|
+
function claimRegistry() {
|
|
1267
|
+
const g = globalThis;
|
|
1268
|
+
let registry = g[ONLOOK_PLUGIN_CLAIMS_GLOBAL];
|
|
1269
|
+
if (!registry) {
|
|
1270
|
+
registry = /* @__PURE__ */ new WeakMap();
|
|
1271
|
+
g[ONLOOK_PLUGIN_CLAIMS_GLOBAL] = registry;
|
|
1272
|
+
}
|
|
1273
|
+
return registry;
|
|
1274
|
+
}
|
|
1275
|
+
function withDedupeApply(plugins, instanceId) {
|
|
1276
|
+
for (const plugin of plugins) {
|
|
1277
|
+
const prior = plugin.apply;
|
|
1278
|
+
plugin.apply = (config, env) => {
|
|
1279
|
+
const priorOk = prior === void 0 ? true : typeof prior === "string" ? env.command === prior : prior(config, env);
|
|
1280
|
+
if (!priorOk) return false;
|
|
1281
|
+
if (env === null || typeof env !== "object") return true;
|
|
1282
|
+
const registry = claimRegistry();
|
|
1283
|
+
const claimed = registry.get(env);
|
|
1284
|
+
if (claimed === void 0) {
|
|
1285
|
+
registry.set(env, instanceId);
|
|
1286
|
+
return true;
|
|
1287
|
+
}
|
|
1288
|
+
return claimed === instanceId;
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
return plugins;
|
|
1292
|
+
}
|
|
646
1293
|
function storybookOnlookPlugin(options = {}) {
|
|
647
1294
|
if (process.env.CHROMATIC || process.env.CI) {
|
|
648
1295
|
console.log("[STORYBOOK_PLUGIN] Disabled in CI/Chromatic environment");
|
|
@@ -650,25 +1297,38 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
650
1297
|
}
|
|
651
1298
|
const port = options.port ?? 6006;
|
|
652
1299
|
const allowedOrigins = [...DEFAULT_ALLOWED_ORIGINS, ...options.allowedOrigins ?? []];
|
|
1300
|
+
const hmrOverride = options.hmr;
|
|
653
1301
|
console.log("[STORYBOOK_PLUGIN] Plugin initialized", {
|
|
654
1302
|
port,
|
|
655
1303
|
allowedOrigins,
|
|
656
1304
|
storybookLocation,
|
|
657
|
-
repoRoot
|
|
1305
|
+
repoRoot,
|
|
1306
|
+
hmr: hmrOverride ?? null
|
|
658
1307
|
});
|
|
1308
|
+
const aliasEntries = options.resolveAliases === false ? [] : resolveTsconfigAliases(process.cwd());
|
|
1309
|
+
if (aliasEntries.length > 0) {
|
|
1310
|
+
console.log("[STORYBOOK_PLUGIN] Resolved tsconfig path aliases", {
|
|
1311
|
+
count: aliasEntries.length,
|
|
1312
|
+
aliases: aliasEntries.map((a) => a.find.source)
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
659
1315
|
const mainPlugin = {
|
|
660
1316
|
name: "storybook-onlook-plugin",
|
|
661
1317
|
config() {
|
|
662
1318
|
return {
|
|
1319
|
+
...aliasEntries.length > 0 && {
|
|
1320
|
+
resolve: { alias: aliasEntries }
|
|
1321
|
+
},
|
|
663
1322
|
server: {
|
|
664
|
-
//
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
1323
|
+
// HMR override applies only when the caller explicitly opts in
|
|
1324
|
+
// (typically: Storybook fronted over HTTPS by a reverse proxy).
|
|
1325
|
+
// Locally, omitting this lets Vite use defaults
|
|
1326
|
+
// (ws://localhost:<port>) and HMR works as expected.
|
|
1327
|
+
...hmrOverride && {
|
|
1328
|
+
hmr: {
|
|
1329
|
+
...hmrOverride,
|
|
1330
|
+
port
|
|
1331
|
+
}
|
|
672
1332
|
},
|
|
673
1333
|
cors: {
|
|
674
1334
|
origin: allowedOrigins
|
|
@@ -679,60 +1339,112 @@ function storybookOnlookPlugin(options = {}) {
|
|
|
679
1339
|
configureServer(server) {
|
|
680
1340
|
console.log("[STORYBOOK_PLUGIN] Configuring server middleware");
|
|
681
1341
|
server.middlewares.use(serveMetadataAndScreenshots);
|
|
1342
|
+
const cachedManifestPath = path7.join(
|
|
1343
|
+
process.cwd(),
|
|
1344
|
+
".storybook-cache",
|
|
1345
|
+
"manifest.json"
|
|
1346
|
+
);
|
|
1347
|
+
refreshManifest(cachedManifestPath);
|
|
1348
|
+
let pending2;
|
|
1349
|
+
const scheduleRefresh = (file) => {
|
|
1350
|
+
if (file !== cachedManifestPath) return;
|
|
1351
|
+
if (pending2) clearTimeout(pending2);
|
|
1352
|
+
pending2 = setTimeout(() => {
|
|
1353
|
+
pending2 = void 0;
|
|
1354
|
+
refreshManifest(cachedManifestPath);
|
|
1355
|
+
}, 100);
|
|
1356
|
+
};
|
|
1357
|
+
server.watcher.on("add", scheduleRefresh);
|
|
1358
|
+
server.watcher.on("change", scheduleRefresh);
|
|
1359
|
+
const storyGlobs = options.storyGlobs ?? ["**/*.stories.@(js|jsx|mjs|ts|tsx|mdx)"];
|
|
1360
|
+
const resolvedStoryGlobs = storyGlobs.map(
|
|
1361
|
+
(glob) => path7.isAbsolute(glob) ? glob : path7.join(process.cwd(), glob)
|
|
1362
|
+
);
|
|
1363
|
+
if (resolvedStoryGlobs.length > 0) {
|
|
1364
|
+
server.watcher.add(resolvedStoryGlobs);
|
|
1365
|
+
console.log("[STORYBOOK_PLUGIN] Extended watcher coverage", {
|
|
1366
|
+
storyGlobs: resolvedStoryGlobs
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
const tailwindEntryPath = (() => {
|
|
1370
|
+
if (options.tailwindEntryCss === false) return null;
|
|
1371
|
+
if (typeof options.tailwindEntryCss === "string") {
|
|
1372
|
+
return path7.isAbsolute(options.tailwindEntryCss) ? options.tailwindEntryCss : path7.join(process.cwd(), options.tailwindEntryCss);
|
|
1373
|
+
}
|
|
1374
|
+
return detectTailwindEntryCss(process.cwd());
|
|
1375
|
+
})();
|
|
1376
|
+
if (tailwindEntryPath) {
|
|
1377
|
+
console.log("[STORYBOOK_PLUGIN] Tailwind rescan watcher enabled", {
|
|
1378
|
+
entry: tailwindEntryPath,
|
|
1379
|
+
autoDetected: options.tailwindEntryCss === void 0
|
|
1380
|
+
});
|
|
1381
|
+
const seenDirs = /* @__PURE__ */ new Set();
|
|
1382
|
+
let watcherReady = false;
|
|
1383
|
+
server.watcher.once("ready", () => {
|
|
1384
|
+
watcherReady = true;
|
|
1385
|
+
});
|
|
1386
|
+
server.watcher.on("add", (file) => {
|
|
1387
|
+
if (!/\.tsx?$/.test(file)) return;
|
|
1388
|
+
const dir = path7.dirname(file);
|
|
1389
|
+
const isNewDir = !seenDirs.has(dir);
|
|
1390
|
+
seenDirs.add(dir);
|
|
1391
|
+
if (!watcherReady || !isNewDir) return;
|
|
1392
|
+
try {
|
|
1393
|
+
const now = /* @__PURE__ */ new Date();
|
|
1394
|
+
fs5.utimesSync(tailwindEntryPath, now, now);
|
|
1395
|
+
console.log("[STORYBOOK_PLUGIN] Tailwind rescan for new dir", {
|
|
1396
|
+
dir,
|
|
1397
|
+
entry: tailwindEntryPath
|
|
1398
|
+
});
|
|
1399
|
+
} catch (err) {
|
|
1400
|
+
console.warn("[STORYBOOK_PLUGIN] Tailwind rescan: utimes failed", {
|
|
1401
|
+
entry: tailwindEntryPath,
|
|
1402
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
682
1407
|
server.httpServer?.once("listening", () => {
|
|
1408
|
+
const probePort = server.config.server.port ?? port;
|
|
683
1409
|
console.log("[STORYBOOK_PLUGIN] Server is listening", {
|
|
684
|
-
port:
|
|
1410
|
+
port: probePort,
|
|
685
1411
|
host: server.config.server.host
|
|
686
1412
|
});
|
|
1413
|
+
void waitForIndexAndBroadcast(probePort);
|
|
687
1414
|
});
|
|
688
1415
|
},
|
|
689
1416
|
configurePreviewServer(server) {
|
|
690
1417
|
console.log("[STORYBOOK_PLUGIN] Configuring preview server middleware");
|
|
691
1418
|
server.middlewares.use(serveMetadataAndScreenshots);
|
|
1419
|
+
refreshManifest(path7.join(process.cwd(), ".storybook-cache", "manifest.json"));
|
|
692
1420
|
},
|
|
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
|
-
}
|
|
1421
|
+
handleHotUpdate: handleStoryFileChange
|
|
699
1422
|
};
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
});
|
|
1423
|
+
const envContainment = envContainmentPlugin(options.envContainment);
|
|
1424
|
+
const moduleLoadCatchAll = options.moduleLoadCatchAll === false ? null : moduleLoadCatchAllPlugin();
|
|
1425
|
+
return withDedupeApply(
|
|
1426
|
+
[
|
|
1427
|
+
...envContainment ? [envContainment] : [],
|
|
1428
|
+
...moduleLoadCatchAll ? [moduleLoadCatchAll] : [],
|
|
1429
|
+
componentLocPlugin(),
|
|
1430
|
+
mainPlugin,
|
|
1431
|
+
fastRefreshTolerantExportsPlugin(),
|
|
1432
|
+
softStoryRerenderPlugin()
|
|
1433
|
+
],
|
|
1434
|
+
/* @__PURE__ */ Symbol("onlook-storybook-plugin-instance")
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
var tolerantCsfIndexer = {
|
|
1438
|
+
test: /(stories|story)\.(m?js|ts)x?$/,
|
|
1439
|
+
createIndex: async (fileName, options) => {
|
|
718
1440
|
try {
|
|
719
|
-
|
|
720
|
-
autoStoryGenerator.vite({
|
|
721
|
-
preset: "react",
|
|
722
|
-
imports,
|
|
723
|
-
ignores,
|
|
724
|
-
storiesFolder: AUTO_STORIES_FOLDER,
|
|
725
|
-
isGenerateStoriesFileAtBuild: true
|
|
726
|
-
})
|
|
727
|
-
);
|
|
1441
|
+
return (await readCsf(fileName, options)).parse().indexInputs;
|
|
728
1442
|
} catch (err) {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
);
|
|
1443
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1444
|
+
console.warn("[onbook] Skipping broken story file:", fileName, msg);
|
|
1445
|
+
return [];
|
|
733
1446
|
}
|
|
734
1447
|
}
|
|
735
|
-
|
|
736
|
-
}
|
|
1448
|
+
};
|
|
737
1449
|
|
|
738
|
-
export {
|
|
1450
|
+
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 };
|