@storybook/react-native 10.3.1 → 10.4.0-canary-20260407095432
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/withStorybook.d.ts +15 -0
- package/dist/withStorybook.js +1304 -0
- package/package.json +5 -4
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { W as WebsocketsOptions } from './index-6iAzVvXp.js';
|
|
2
|
+
|
|
3
|
+
interface WithStorybookOptions {
|
|
4
|
+
configPath?: string;
|
|
5
|
+
websockets?: WebsocketsOptions | 'auto';
|
|
6
|
+
useJs?: boolean;
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
docTools?: boolean;
|
|
9
|
+
liteMode?: boolean;
|
|
10
|
+
experimental_mcp?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare function withStorybook<T>(config: T, options?: WithStorybookOptions): T;
|
|
14
|
+
|
|
15
|
+
export { withStorybook };
|
|
@@ -0,0 +1,1304 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __esm = (fn, res) => function __init() {
|
|
8
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
9
|
+
};
|
|
10
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
11
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
12
|
+
};
|
|
13
|
+
var __export = (target, all) => {
|
|
14
|
+
for (var name in all)
|
|
15
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
26
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
31
|
+
mod
|
|
32
|
+
));
|
|
33
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
34
|
+
|
|
35
|
+
// scripts/common.js
|
|
36
|
+
var require_common = __commonJS({
|
|
37
|
+
"scripts/common.js"(exports2, module2) {
|
|
38
|
+
var { globToRegexp } = require("storybook/internal/common");
|
|
39
|
+
var path5 = require("path");
|
|
40
|
+
var fs2 = require("fs");
|
|
41
|
+
var cwd2 = process.cwd();
|
|
42
|
+
var toRequireContext = (specifier) => {
|
|
43
|
+
const { directory, files } = specifier;
|
|
44
|
+
const match = globToRegexp(`./${files}`);
|
|
45
|
+
return {
|
|
46
|
+
path: directory,
|
|
47
|
+
recursive: files.includes("**") || files.split("/").length > 1,
|
|
48
|
+
match
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
var supportedExtensions = ["js", "jsx", "ts", "tsx", "cjs", "mjs"];
|
|
52
|
+
function getFilePathExtension({ configPath }, fileName) {
|
|
53
|
+
for (const ext of supportedExtensions) {
|
|
54
|
+
const filePath = path5.resolve(cwd2, configPath, `${fileName}.${ext}`);
|
|
55
|
+
if (fs2.existsSync(filePath)) {
|
|
56
|
+
return ext;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function getFilePathWithExtension2({ configPath }, fileName) {
|
|
62
|
+
for (const ext of supportedExtensions) {
|
|
63
|
+
const filePath = path5.resolve(cwd2, configPath, `${fileName}.${ext}`);
|
|
64
|
+
if (fs2.existsSync(filePath)) {
|
|
65
|
+
return filePath;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
function ensureRelativePathHasDot2(relativePath) {
|
|
71
|
+
return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
72
|
+
}
|
|
73
|
+
function getPreviewExists({ configPath }) {
|
|
74
|
+
return !!getFilePathExtension({ configPath }, "preview");
|
|
75
|
+
}
|
|
76
|
+
function resolveAddonFile(addon, file, extensions = ["js", "mjs", "ts"], configPath) {
|
|
77
|
+
if (!addon || typeof addon !== "string") return null;
|
|
78
|
+
const resolvePaths = { paths: [cwd2] };
|
|
79
|
+
try {
|
|
80
|
+
const basePath = `${addon}/${file}`;
|
|
81
|
+
require.resolve(basePath, resolvePaths);
|
|
82
|
+
return basePath;
|
|
83
|
+
} catch (_error) {
|
|
84
|
+
}
|
|
85
|
+
for (const ext of extensions) {
|
|
86
|
+
try {
|
|
87
|
+
const filePath = `${addon}/${file}.${ext}`;
|
|
88
|
+
require.resolve(filePath, resolvePaths);
|
|
89
|
+
return filePath;
|
|
90
|
+
} catch (_error) {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (addon.startsWith("./") || addon.startsWith("../")) {
|
|
94
|
+
try {
|
|
95
|
+
const extension = getFilePathExtension({ configPath }, `${addon}/${file}`);
|
|
96
|
+
if (extension) {
|
|
97
|
+
return `${addon}/${file}`;
|
|
98
|
+
}
|
|
99
|
+
} catch (_error) {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
function getAddonName(addon) {
|
|
105
|
+
if (typeof addon === "string") return addon;
|
|
106
|
+
if (typeof addon === "object" && addon.name && typeof addon.name === "string") return addon.name;
|
|
107
|
+
console.error("Invalid addon configuration", addon);
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
module2.exports = {
|
|
111
|
+
toRequireContext,
|
|
112
|
+
getFilePathExtension,
|
|
113
|
+
ensureRelativePathHasDot: ensureRelativePathHasDot2,
|
|
114
|
+
getPreviewExists,
|
|
115
|
+
resolveAddonFile,
|
|
116
|
+
getAddonName,
|
|
117
|
+
getFilePathWithExtension: getFilePathWithExtension2
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// scripts/require-interop.js
|
|
123
|
+
var require_require_interop = __commonJS({
|
|
124
|
+
"scripts/require-interop.js"(exports2, module2) {
|
|
125
|
+
var registered = false;
|
|
126
|
+
function interopRequireDefault(filePath) {
|
|
127
|
+
const hasEsbuildBeenRegistered = !!require("module")._extensions[".ts"];
|
|
128
|
+
if (registered === false && !hasEsbuildBeenRegistered) {
|
|
129
|
+
const { register } = require("esbuild-register/dist/node");
|
|
130
|
+
registered = true;
|
|
131
|
+
register({
|
|
132
|
+
target: `node${process.version.slice(1)}`,
|
|
133
|
+
format: "cjs",
|
|
134
|
+
hookIgnoreNodeModules: true,
|
|
135
|
+
// Some frameworks, like Stylus, rely on the 'name' property of classes or functions
|
|
136
|
+
// https://github.com/storybookjs/storybook/issues/19049
|
|
137
|
+
keepNames: true,
|
|
138
|
+
tsconfigRaw: `{
|
|
139
|
+
"compilerOptions": {
|
|
140
|
+
"strict": false,
|
|
141
|
+
"skipLibCheck": true,
|
|
142
|
+
},
|
|
143
|
+
}`
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const result = require(filePath);
|
|
147
|
+
const isES6DefaultExported = typeof result === "object" && result !== null && typeof result.default !== "undefined";
|
|
148
|
+
return isES6DefaultExported ? result.default : result;
|
|
149
|
+
}
|
|
150
|
+
module2.exports = { interopRequireDefault };
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// scripts/generate.js
|
|
155
|
+
var require_generate = __commonJS({
|
|
156
|
+
"scripts/generate.js"(exports2, module2) {
|
|
157
|
+
var {
|
|
158
|
+
toRequireContext,
|
|
159
|
+
ensureRelativePathHasDot: ensureRelativePathHasDot2,
|
|
160
|
+
getPreviewExists,
|
|
161
|
+
resolveAddonFile,
|
|
162
|
+
getAddonName
|
|
163
|
+
} = require_common();
|
|
164
|
+
var { normalizeStories: normalizeStories2, globToRegexp, loadMainConfig: loadMainConfig2 } = require("storybook/internal/common");
|
|
165
|
+
var { interopRequireDefault } = require_require_interop();
|
|
166
|
+
var fs2 = require("fs");
|
|
167
|
+
var { networkInterfaces } = require("os");
|
|
168
|
+
var path5 = require("path");
|
|
169
|
+
var cwd2 = process.cwd();
|
|
170
|
+
var loadMain = async ({ configPath, cwd: cwd3 }) => {
|
|
171
|
+
try {
|
|
172
|
+
const main = await loadMainConfig2({ configDir: configPath, cwd: cwd3 });
|
|
173
|
+
return main;
|
|
174
|
+
} catch {
|
|
175
|
+
console.error("Error loading main config, trying fallback");
|
|
176
|
+
}
|
|
177
|
+
const mainPathTs = path5.resolve(cwd3, configPath, `main.ts`);
|
|
178
|
+
const mainPathJs = path5.resolve(cwd3, configPath, `main.js`);
|
|
179
|
+
if (fs2.existsSync(mainPathTs)) {
|
|
180
|
+
return interopRequireDefault(mainPathTs);
|
|
181
|
+
} else if (fs2.existsSync(mainPathJs)) {
|
|
182
|
+
return interopRequireDefault(mainPathJs);
|
|
183
|
+
} else {
|
|
184
|
+
throw new Error(`Main config file not found at ${mainPathTs} or ${mainPathJs}`);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
function getLocalIPAddress() {
|
|
188
|
+
const nets = networkInterfaces();
|
|
189
|
+
for (const name of Object.keys(nets)) {
|
|
190
|
+
for (const net of nets[name]) {
|
|
191
|
+
const familyV4Value = typeof net.family === "string" ? "IPv4" : 4;
|
|
192
|
+
if (net.family === familyV4Value && !net.internal) {
|
|
193
|
+
return net.address;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return "0.0.0.0";
|
|
198
|
+
}
|
|
199
|
+
async function generate2({
|
|
200
|
+
configPath,
|
|
201
|
+
useJs = false,
|
|
202
|
+
docTools = true,
|
|
203
|
+
host = void 0,
|
|
204
|
+
port = void 0,
|
|
205
|
+
secured = false
|
|
206
|
+
}) {
|
|
207
|
+
const channelHost = host === "auto" ? getLocalIPAddress() : host;
|
|
208
|
+
const storybookRequiresLocation = path5.resolve(
|
|
209
|
+
cwd2,
|
|
210
|
+
configPath,
|
|
211
|
+
`storybook.requires.${useJs ? "js" : "ts"}`
|
|
212
|
+
);
|
|
213
|
+
const main = await loadMain({ configPath, cwd: cwd2 });
|
|
214
|
+
const storiesSpecifiers = normalizeStories2(main.stories, {
|
|
215
|
+
configDir: configPath,
|
|
216
|
+
workingDir: cwd2
|
|
217
|
+
});
|
|
218
|
+
const normalizedStories = storiesSpecifiers.map((specifier) => {
|
|
219
|
+
const reg = globToRegexp(`./${specifier.files}`);
|
|
220
|
+
const { path: p, recursive: r, match: m } = toRequireContext(specifier);
|
|
221
|
+
const pathToStory = ensureRelativePathHasDot2(path5.posix.relative(configPath, p));
|
|
222
|
+
return `{
|
|
223
|
+
titlePrefix: "${specifier.titlePrefix}",
|
|
224
|
+
directory: "${specifier.directory}",
|
|
225
|
+
files: "${specifier.files}",
|
|
226
|
+
importPathMatcher: /${reg.source}/,
|
|
227
|
+
req: require.context(
|
|
228
|
+
'${pathToStory}',
|
|
229
|
+
${r},
|
|
230
|
+
${m}
|
|
231
|
+
),
|
|
232
|
+
}`;
|
|
233
|
+
});
|
|
234
|
+
const registeredAddons = [];
|
|
235
|
+
for (const addon of main.addons) {
|
|
236
|
+
const registerPath = resolveAddonFile(
|
|
237
|
+
getAddonName(addon),
|
|
238
|
+
"register",
|
|
239
|
+
["js", "mjs", "jsx", "ts", "tsx"],
|
|
240
|
+
configPath
|
|
241
|
+
);
|
|
242
|
+
if (registerPath) {
|
|
243
|
+
registeredAddons.push(`import "${registerPath}";`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const docToolsAnnotation = 'require("@storybook/react-native/preview")';
|
|
247
|
+
const enhancers = [];
|
|
248
|
+
if (docTools) {
|
|
249
|
+
enhancers.push(docToolsAnnotation);
|
|
250
|
+
}
|
|
251
|
+
for (const addon of main.addons) {
|
|
252
|
+
const previewPath = resolveAddonFile(
|
|
253
|
+
getAddonName(addon),
|
|
254
|
+
"preview",
|
|
255
|
+
["js", "mjs", "jsx", "ts", "tsx"],
|
|
256
|
+
configPath
|
|
257
|
+
);
|
|
258
|
+
if (previewPath) {
|
|
259
|
+
enhancers.push(`require('${previewPath}')`);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
let options = "";
|
|
264
|
+
let optionsVar = "";
|
|
265
|
+
const reactNativeOptions = main.reactNative;
|
|
266
|
+
if (reactNativeOptions && typeof reactNativeOptions === "object") {
|
|
267
|
+
optionsVar = `const options = ${JSON.stringify(reactNativeOptions, null, 2)}`;
|
|
268
|
+
options = "options";
|
|
269
|
+
}
|
|
270
|
+
let featuresAssignment = "";
|
|
271
|
+
const features = main.features;
|
|
272
|
+
if (features && typeof features === "object") {
|
|
273
|
+
const featureEntries = Object.entries(features).filter(
|
|
274
|
+
([, value]) => typeof value === "boolean"
|
|
275
|
+
);
|
|
276
|
+
if (featureEntries.length > 0) {
|
|
277
|
+
const assignments = featureEntries.map(([key, value]) => `globalThis.FEATURES.${key} = ${value};`).join("\n");
|
|
278
|
+
featuresAssignment = assignments;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const previewExists = getPreviewExists({ configPath });
|
|
282
|
+
if (previewExists) {
|
|
283
|
+
enhancers.unshift("require('./preview')");
|
|
284
|
+
}
|
|
285
|
+
const annotations = `[
|
|
286
|
+
${enhancers.join(",\n ")}
|
|
287
|
+
]`;
|
|
288
|
+
const hasWebsocketConfig = host !== void 0 || port !== void 0 || secured;
|
|
289
|
+
const websocketAssignmentLines = [];
|
|
290
|
+
if (channelHost) {
|
|
291
|
+
websocketAssignmentLines.push(`host: '${channelHost}',`);
|
|
292
|
+
}
|
|
293
|
+
if (hasWebsocketConfig) {
|
|
294
|
+
websocketAssignmentLines.push(`port: ${port ?? 7007},`);
|
|
295
|
+
websocketAssignmentLines.push(`secured: ${Boolean(secured)},`);
|
|
296
|
+
}
|
|
297
|
+
const globalTypes = `
|
|
298
|
+
declare global {
|
|
299
|
+
var view: View;
|
|
300
|
+
var STORIES: typeof normalizedStories;
|
|
301
|
+
var STORYBOOK_WEBSOCKET:
|
|
302
|
+
| { host?: string; port?: number; secured?: boolean }
|
|
303
|
+
| undefined;
|
|
304
|
+
var FEATURES: Features;
|
|
305
|
+
}
|
|
306
|
+
`;
|
|
307
|
+
const fileContent = `/* do not change this file, it is auto generated by storybook. */
|
|
308
|
+
${useJs ? "" : '/// <reference types="@storybook/react-native/metro-env" />\n'}import { start, updateView${useJs ? "" : ", View, type Features"} } from '@storybook/react-native';
|
|
309
|
+
|
|
310
|
+
${registeredAddons.join("\n")}
|
|
311
|
+
|
|
312
|
+
const normalizedStories = [
|
|
313
|
+
${normalizedStories.join(",\n ")}
|
|
314
|
+
];
|
|
315
|
+
|
|
316
|
+
${useJs ? "" : globalTypes}
|
|
317
|
+
|
|
318
|
+
const annotations = ${annotations};
|
|
319
|
+
|
|
320
|
+
globalThis.STORIES = normalizedStories;
|
|
321
|
+
${hasWebsocketConfig ? `globalThis.STORYBOOK_WEBSOCKET = {
|
|
322
|
+
${websocketAssignmentLines.join("\n ")}
|
|
323
|
+
};` : ""}
|
|
324
|
+
|
|
325
|
+
module?.hot?.accept?.();
|
|
326
|
+
${featuresAssignment ? `
|
|
327
|
+
${featuresAssignment}
|
|
328
|
+
` : ""}
|
|
329
|
+
${optionsVar}
|
|
330
|
+
|
|
331
|
+
if (!globalThis.view) {
|
|
332
|
+
globalThis.view = start({
|
|
333
|
+
annotations,
|
|
334
|
+
storyEntries: normalizedStories,
|
|
335
|
+
${options ? ` ${options},` : ""}
|
|
336
|
+
});
|
|
337
|
+
} else {
|
|
338
|
+
updateView(globalThis.view, annotations, normalizedStories${options ? `, ${options}` : ""});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export const view${useJs ? "" : ": View"} = globalThis.view;
|
|
342
|
+
`;
|
|
343
|
+
fs2.writeFileSync(storybookRequiresLocation, fileContent, {
|
|
344
|
+
encoding: "utf8",
|
|
345
|
+
flag: "w"
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
module2.exports = {
|
|
349
|
+
generate: generate2
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// src/metro/buildIndex.ts
|
|
355
|
+
var buildIndex_exports = {};
|
|
356
|
+
__export(buildIndex_exports, {
|
|
357
|
+
buildIndex: () => buildIndex
|
|
358
|
+
});
|
|
359
|
+
function ensureRelativePathHasDot(relativePath) {
|
|
360
|
+
return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
361
|
+
}
|
|
362
|
+
async function buildIndex({ configPath }) {
|
|
363
|
+
const main = await (0, import_common.loadMainConfig)({ configDir: configPath, cwd });
|
|
364
|
+
if (!main.stories || !Array.isArray(main.stories)) {
|
|
365
|
+
throw new Error("No stories found");
|
|
366
|
+
}
|
|
367
|
+
const storiesSpecifiers = (0, import_common.normalizeStories)(main.stories, {
|
|
368
|
+
configDir: configPath,
|
|
369
|
+
workingDir: cwd
|
|
370
|
+
});
|
|
371
|
+
const specifierStoryPaths = storiesSpecifiers.map((specifier) => {
|
|
372
|
+
return (0, import_glob.sync)(specifier.files, {
|
|
373
|
+
cwd: import_path.default.resolve(process.cwd(), specifier.directory),
|
|
374
|
+
absolute: true,
|
|
375
|
+
// default to always ignore (exclude) anything in node_modules
|
|
376
|
+
ignore: ["**/node_modules"]
|
|
377
|
+
}).map((storyPath) => {
|
|
378
|
+
const normalizePathForWindows = (str) => import_path.default.sep === "\\" ? str.replace(/\\/g, "/") : str;
|
|
379
|
+
return normalizePathForWindows(storyPath);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
const csfStories = specifierStoryPaths.reduce(
|
|
383
|
+
(acc, specifierStoryPathList, specifierIndex) => {
|
|
384
|
+
const paths = specifierStoryPathList.map((storyPath) => {
|
|
385
|
+
const code = (0, import_node_fs.readFileSync)(storyPath, { encoding: "utf-8" }).toString();
|
|
386
|
+
const relativePath = ensureRelativePathHasDot(import_path.default.posix.relative(cwd, storyPath));
|
|
387
|
+
return {
|
|
388
|
+
result: (0, import_csf_tools.loadCsf)(code, {
|
|
389
|
+
fileName: storyPath,
|
|
390
|
+
makeTitle: (userTitle) => makeTitle(relativePath, storiesSpecifiers[specifierIndex], userTitle)
|
|
391
|
+
}).parse(),
|
|
392
|
+
specifier: storiesSpecifiers[specifierIndex],
|
|
393
|
+
fileName: relativePath
|
|
394
|
+
};
|
|
395
|
+
});
|
|
396
|
+
return [...acc, ...paths];
|
|
397
|
+
},
|
|
398
|
+
new Array()
|
|
399
|
+
);
|
|
400
|
+
const index = {
|
|
401
|
+
v: 5,
|
|
402
|
+
entries: {}
|
|
403
|
+
};
|
|
404
|
+
for (const { result, specifier, fileName } of csfStories) {
|
|
405
|
+
const { meta, stories } = result;
|
|
406
|
+
if (stories && stories.length > 0) {
|
|
407
|
+
for (const story of stories) {
|
|
408
|
+
const id = story.id ?? (0, import_csf.toId)(meta.title, story.name);
|
|
409
|
+
if (!id) {
|
|
410
|
+
throw new Error(`Failed to generate id for story ${story.name} in file ${fileName}`);
|
|
411
|
+
}
|
|
412
|
+
index.entries[id] = {
|
|
413
|
+
type: "story",
|
|
414
|
+
subtype: "story",
|
|
415
|
+
id,
|
|
416
|
+
name: story.name,
|
|
417
|
+
title: meta.title,
|
|
418
|
+
importPath: `${specifier.directory}/${import_path.default.posix.relative(specifier.directory, fileName)}`,
|
|
419
|
+
tags: ["story"]
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
} else {
|
|
423
|
+
console.log(`No stories found for ${fileName}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
const previewPath = (0, import_common2.getFilePathWithExtension)({ configPath }, "preview");
|
|
428
|
+
const previewSourceCode = (0, import_node_fs.readFileSync)(previewPath, { encoding: "utf-8" }).toString();
|
|
429
|
+
const storySort = (0, import_csf_tools.getStorySortParameter)(previewSourceCode);
|
|
430
|
+
const sortableStories = Object.values(index.entries);
|
|
431
|
+
(0, import_preview_api.sortStoriesV7)(
|
|
432
|
+
sortableStories,
|
|
433
|
+
storySort,
|
|
434
|
+
sortableStories.map((entry) => entry.importPath)
|
|
435
|
+
);
|
|
436
|
+
const sorted = sortableStories.reduce(
|
|
437
|
+
(acc, item) => {
|
|
438
|
+
acc[item.id] = item;
|
|
439
|
+
return acc;
|
|
440
|
+
},
|
|
441
|
+
{}
|
|
442
|
+
);
|
|
443
|
+
return { v: 5, entries: sorted };
|
|
444
|
+
} catch {
|
|
445
|
+
console.warn("Failed to sort stories, using unordered index");
|
|
446
|
+
return index;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
var import_common, import_node_fs, import_glob, import_path, import_csf_tools, import_csf, import_preview_api, import_common2, cwd, makeTitle;
|
|
450
|
+
var init_buildIndex = __esm({
|
|
451
|
+
"src/metro/buildIndex.ts"() {
|
|
452
|
+
import_common = require("storybook/internal/common");
|
|
453
|
+
import_node_fs = require("fs");
|
|
454
|
+
import_glob = require("glob");
|
|
455
|
+
import_path = __toESM(require("path"));
|
|
456
|
+
import_csf_tools = require("storybook/internal/csf-tools");
|
|
457
|
+
import_csf = require("storybook/internal/csf");
|
|
458
|
+
import_preview_api = require("storybook/internal/preview-api");
|
|
459
|
+
import_common2 = __toESM(require_common());
|
|
460
|
+
cwd = process.cwd();
|
|
461
|
+
makeTitle = (fileName, specifier, userTitle) => {
|
|
462
|
+
const title = (0, import_preview_api.userOrAutoTitleFromSpecifier)(fileName, specifier, userTitle);
|
|
463
|
+
if (title) {
|
|
464
|
+
return title.replace("./", "");
|
|
465
|
+
} else if (userTitle) {
|
|
466
|
+
return userTitle.replace("./", "");
|
|
467
|
+
} else {
|
|
468
|
+
console.error("Could not generate title!!");
|
|
469
|
+
process.exit(1);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// src/metro/manifest/storyInstructions.ts
|
|
476
|
+
var storyInstructions_exports = {};
|
|
477
|
+
__export(storyInstructions_exports, {
|
|
478
|
+
storyInstructions: () => storyInstructions
|
|
479
|
+
});
|
|
480
|
+
var storyInstructions;
|
|
481
|
+
var init_storyInstructions = __esm({
|
|
482
|
+
"src/metro/manifest/storyInstructions.ts"() {
|
|
483
|
+
storyInstructions = `# Writing React Native UI Components
|
|
484
|
+
|
|
485
|
+
When writing UI, prefer breaking larger components up into smaller parts.
|
|
486
|
+
|
|
487
|
+
ALWAYS write a Storybook story for any component written. If editing a component, ensure appropriate changes have been made to stories for that component.
|
|
488
|
+
|
|
489
|
+
## How to write good stories
|
|
490
|
+
|
|
491
|
+
Goal: Cover every distinct piece of business logic and state the component can reach (happy paths, error/edge states, loading, permissions/roles, empty states, variations from props/context). Avoid redundant stories that show the same logic.
|
|
492
|
+
|
|
493
|
+
Interactivity: For interactive components, create separate stories that demonstrate each interaction state. Use \`fn()\` from \`storybook/test\` to mock callback props so you can verify they are wired up correctly.
|
|
494
|
+
|
|
495
|
+
Data/setup: Provide realistic props, state, and mocked data. Include meaningful labels/text to make behaviors observable. Stub network/services with deterministic fixtures; keep stories reliable.
|
|
496
|
+
|
|
497
|
+
Variants to consider (pick only those that change behavior): default vs. alternate themes; loading vs. loaded vs. empty vs. error; validated vs. invalid input; permissions/roles/capabilities; feature flags; size/density/layout variants that alter logic.
|
|
498
|
+
|
|
499
|
+
Accessibility: Use semantic roles/labels where applicable.
|
|
500
|
+
|
|
501
|
+
Naming/structure: Use clear story names that describe the scenario ("Error state after failed submit"). Group related variants logically; don't duplicate.
|
|
502
|
+
|
|
503
|
+
Imports/format: Import Meta/StoryObj from the framework package. Keep stories minimal\u2014only what's needed to demonstrate behavior.
|
|
504
|
+
|
|
505
|
+
## React Native Storybook Essentials
|
|
506
|
+
|
|
507
|
+
### Framework and Renderer
|
|
508
|
+
|
|
509
|
+
React Native Storybook uses \`@storybook/react-native\` as the framework. Stories use the same CSF (Component Story Format) as web Storybook.
|
|
510
|
+
|
|
511
|
+
### Meta and StoryObj imports
|
|
512
|
+
|
|
513
|
+
\`\`\`ts
|
|
514
|
+
import type { Meta, StoryObj } from '@storybook/react-native';
|
|
515
|
+
\`\`\`
|
|
516
|
+
|
|
517
|
+
### Story file structure
|
|
518
|
+
|
|
519
|
+
\`\`\`tsx
|
|
520
|
+
import type { Meta, StoryObj } from '@storybook/react-native';
|
|
521
|
+
import { MyComponent } from './MyComponent';
|
|
522
|
+
|
|
523
|
+
const meta: Meta<typeof MyComponent> = {
|
|
524
|
+
title: 'Components/MyComponent',
|
|
525
|
+
component: MyComponent,
|
|
526
|
+
args: {
|
|
527
|
+
// default args
|
|
528
|
+
},
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
export default meta;
|
|
532
|
+
|
|
533
|
+
type Story = StoryObj<typeof meta>;
|
|
534
|
+
|
|
535
|
+
export const Default: Story = {};
|
|
536
|
+
|
|
537
|
+
export const WithCustomProps: Story = {
|
|
538
|
+
args: {
|
|
539
|
+
label: 'Custom Label',
|
|
540
|
+
variant: 'secondary',
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
\`\`\`
|
|
544
|
+
|
|
545
|
+
### Global State Changes
|
|
546
|
+
|
|
547
|
+
The \`globals\` annotation has been renamed to \`initialGlobals\`:
|
|
548
|
+
|
|
549
|
+
\`\`\`diff
|
|
550
|
+
// .rnstorybook/preview.js
|
|
551
|
+
export default {
|
|
552
|
+
- globals: { theme: 'light' }
|
|
553
|
+
+ initialGlobals: { theme: 'light' }
|
|
554
|
+
};
|
|
555
|
+
\`\`\`
|
|
556
|
+
|
|
557
|
+
### React Native Specific Considerations
|
|
558
|
+
|
|
559
|
+
- The config directory is \`.rnstorybook\` (not \`.storybook\`)
|
|
560
|
+
- Stories run on-device (iOS/Android), not in a browser
|
|
561
|
+
- Use React Native components (\`View\`, \`Text\`, \`Pressable\`, etc.), not HTML elements
|
|
562
|
+
- \`StyleSheet\` or inline styles instead of CSS
|
|
563
|
+
- No DOM APIs \u2014 use React Native's layout system (Flexbox)
|
|
564
|
+
- For navigation-dependent components, mock the navigation context
|
|
565
|
+
- For platform-specific stories, use \`Platform.OS\` checks or separate story files
|
|
566
|
+
- Test on both iOS and Android when possible
|
|
567
|
+
|
|
568
|
+
### Key Requirements
|
|
569
|
+
|
|
570
|
+
- **Node.js 20+**, **TypeScript 4.9+**
|
|
571
|
+
- React Native 0.72+
|
|
572
|
+
- Storybook 10+
|
|
573
|
+
`;
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
// src/withStorybook.ts
|
|
578
|
+
var withStorybook_exports = {};
|
|
579
|
+
__export(withStorybook_exports, {
|
|
580
|
+
withStorybook: () => withStorybook
|
|
581
|
+
});
|
|
582
|
+
module.exports = __toCommonJS(withStorybook_exports);
|
|
583
|
+
var path4 = __toESM(require("path"));
|
|
584
|
+
|
|
585
|
+
// src/enhanceMetroConfig.ts
|
|
586
|
+
var path = __toESM(require("path"));
|
|
587
|
+
function enhanceMetroConfig(config, options = {}) {
|
|
588
|
+
const { liteMode = false, swap } = options;
|
|
589
|
+
return {
|
|
590
|
+
...config,
|
|
591
|
+
transformer: {
|
|
592
|
+
...config.transformer,
|
|
593
|
+
unstable_allowRequireContext: true
|
|
594
|
+
},
|
|
595
|
+
resolver: {
|
|
596
|
+
...config.resolver,
|
|
597
|
+
resolveRequest: (context, moduleName, platform) => {
|
|
598
|
+
const resolveFunction = config?.resolver?.resolveRequest ? config.resolver.resolveRequest : context.resolveRequest;
|
|
599
|
+
const shouldUseCustomResolveConfig = moduleName.startsWith("storybook") || moduleName.startsWith("@storybook") || moduleName.startsWith("uuid");
|
|
600
|
+
const theContext = shouldUseCustomResolveConfig ? {
|
|
601
|
+
...context,
|
|
602
|
+
unstable_enablePackageExports: true,
|
|
603
|
+
unstable_conditionNames: ["import"]
|
|
604
|
+
} : context;
|
|
605
|
+
const resolveResult = resolveFunction(theContext, moduleName, platform);
|
|
606
|
+
if (resolveResult?.filePath?.includes?.("@storybook/react/template/cli")) {
|
|
607
|
+
return { type: "empty" };
|
|
608
|
+
}
|
|
609
|
+
if (moduleName === "tty" || moduleName === "os") {
|
|
610
|
+
return { type: "empty" };
|
|
611
|
+
}
|
|
612
|
+
if (liteMode && resolveResult?.filePath?.includes?.("@storybook/react-native-ui") && !resolveResult?.filePath?.includes?.("@storybook/react-native-ui-lite") && !resolveResult?.filePath?.includes?.("@storybook/react-native-ui-common")) {
|
|
613
|
+
return { type: "empty" };
|
|
614
|
+
}
|
|
615
|
+
if (swap && resolveResult?.filePath && path.resolve(resolveResult.filePath) === swap.appEntryPoint) {
|
|
616
|
+
return {
|
|
617
|
+
filePath: swap.storybookEntryPoint,
|
|
618
|
+
type: "sourceFile"
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
return resolveResult;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// src/enhanceRepackConfig.ts
|
|
628
|
+
function enhanceRepackConfig(config, options = {}) {
|
|
629
|
+
const { swap, liteMode = false } = options;
|
|
630
|
+
if (!swap) {
|
|
631
|
+
return config;
|
|
632
|
+
}
|
|
633
|
+
const result = {
|
|
634
|
+
...config,
|
|
635
|
+
entry: swap.storybookEntryPoint
|
|
636
|
+
};
|
|
637
|
+
if (liteMode) {
|
|
638
|
+
const resolve4 = result.resolve ?? {};
|
|
639
|
+
const alias = resolve4.alias ?? {};
|
|
640
|
+
alias["@storybook/react-native-ui$"] = false;
|
|
641
|
+
result.resolve = { ...resolve4, alias };
|
|
642
|
+
}
|
|
643
|
+
return result;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// src/metro/utils.ts
|
|
647
|
+
var path2 = __toESM(require("path"));
|
|
648
|
+
var fs = __toESM(require("fs"));
|
|
649
|
+
var ENTRY_EXTENSIONS = ["js", "jsx", "ts", "tsx"];
|
|
650
|
+
function resolveEntryPoint(projectRoot = process.cwd()) {
|
|
651
|
+
const pkgJsonPath = path2.resolve(projectRoot, "package.json");
|
|
652
|
+
let mainField;
|
|
653
|
+
try {
|
|
654
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
655
|
+
mainField = pkgJson.main;
|
|
656
|
+
if (mainField === "expo-router/entry") {
|
|
657
|
+
const expoRouterEntry = resolveFileWithExtensions(
|
|
658
|
+
path2.resolve(projectRoot, "node_modules", "expo-router", "entry"),
|
|
659
|
+
ENTRY_EXTENSIONS
|
|
660
|
+
);
|
|
661
|
+
if (expoRouterEntry) {
|
|
662
|
+
return expoRouterEntry;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
} catch {
|
|
666
|
+
}
|
|
667
|
+
if (mainField && mainField !== "expo-router/entry") {
|
|
668
|
+
const resolved = resolveFileWithExtensions(
|
|
669
|
+
path2.resolve(projectRoot, mainField),
|
|
670
|
+
ENTRY_EXTENSIONS
|
|
671
|
+
);
|
|
672
|
+
if (resolved) {
|
|
673
|
+
return resolved;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
const fallback = resolveFileWithExtensions(path2.resolve(projectRoot, "index"), ENTRY_EXTENSIONS);
|
|
677
|
+
return fallback;
|
|
678
|
+
}
|
|
679
|
+
function resolveFileWithExtensions(basePath, extensions) {
|
|
680
|
+
try {
|
|
681
|
+
if (fs.statSync(basePath).isFile()) {
|
|
682
|
+
return basePath;
|
|
683
|
+
}
|
|
684
|
+
} catch {
|
|
685
|
+
}
|
|
686
|
+
for (const ext of extensions) {
|
|
687
|
+
const candidate = `${basePath}.${ext}`;
|
|
688
|
+
if (fs.existsSync(candidate)) {
|
|
689
|
+
return candidate;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return void 0;
|
|
693
|
+
}
|
|
694
|
+
function resolveStorybookEntry(configPath) {
|
|
695
|
+
return resolveFileWithExtensions(path2.resolve(configPath, "index"), ENTRY_EXTENSIONS);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// src/withStorybook.ts
|
|
699
|
+
var import_generate = __toESM(require_generate());
|
|
700
|
+
|
|
701
|
+
// src/metro/channelServer.ts
|
|
702
|
+
var import_ws2 = require("ws");
|
|
703
|
+
var import_node_http = require("http");
|
|
704
|
+
var import_node_https = require("https");
|
|
705
|
+
init_buildIndex();
|
|
706
|
+
|
|
707
|
+
// src/metro/mcpServer.ts
|
|
708
|
+
var import_consumers = require("stream/consumers");
|
|
709
|
+
function toHeaderEntries(nodeHeaders) {
|
|
710
|
+
const entries = [];
|
|
711
|
+
for (const [key, value] of Object.entries(nodeHeaders)) {
|
|
712
|
+
if (value === void 0) continue;
|
|
713
|
+
entries.push([key, Array.isArray(value) ? value.join(", ") : value]);
|
|
714
|
+
}
|
|
715
|
+
return entries;
|
|
716
|
+
}
|
|
717
|
+
async function incomingMessageToWebRequest(req) {
|
|
718
|
+
const host = req.headers.host || "localhost";
|
|
719
|
+
const isTLS = "encrypted" in req.socket && req.socket.encrypted;
|
|
720
|
+
const protocol = isTLS ? "https" : "http";
|
|
721
|
+
const url = new URL(req.url || "/", `${protocol}://${host}`);
|
|
722
|
+
const bodyBuffer = await (0, import_consumers.buffer)(req);
|
|
723
|
+
return new Request(url, {
|
|
724
|
+
method: req.method,
|
|
725
|
+
headers: toHeaderEntries(req.headers),
|
|
726
|
+
body: bodyBuffer.length > 0 ? new Uint8Array(bodyBuffer) : void 0
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
async function webResponseToServerResponse(webResponse, nodeResponse) {
|
|
730
|
+
nodeResponse.statusCode = webResponse.status;
|
|
731
|
+
webResponse.headers.forEach((value, key) => {
|
|
732
|
+
nodeResponse.setHeader(key, value);
|
|
733
|
+
});
|
|
734
|
+
if (webResponse.body) {
|
|
735
|
+
const reader = webResponse.body.getReader();
|
|
736
|
+
try {
|
|
737
|
+
while (true) {
|
|
738
|
+
const { done, value } = await reader.read();
|
|
739
|
+
if (done) break;
|
|
740
|
+
nodeResponse.write(value);
|
|
741
|
+
}
|
|
742
|
+
} finally {
|
|
743
|
+
reader.releaseLock();
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
nodeResponse.end();
|
|
747
|
+
}
|
|
748
|
+
function createMcpHandler(configPath, wss) {
|
|
749
|
+
let handler = null;
|
|
750
|
+
let initPromise = null;
|
|
751
|
+
async function init() {
|
|
752
|
+
if (handler) return;
|
|
753
|
+
if (initPromise) {
|
|
754
|
+
await initPromise;
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
initPromise = (async () => {
|
|
758
|
+
try {
|
|
759
|
+
const [
|
|
760
|
+
{ McpServer },
|
|
761
|
+
{ ValibotJsonSchemaAdapter },
|
|
762
|
+
{ HttpTransport },
|
|
763
|
+
{ addListAllDocumentationTool, addGetDocumentationTool, addGetStoryDocumentationTool },
|
|
764
|
+
{ storyInstructions: storyInstructions2 },
|
|
765
|
+
{ buildIndex: buildIndex2 },
|
|
766
|
+
valibot,
|
|
767
|
+
{ experimental_manifests }
|
|
768
|
+
] = await Promise.all([
|
|
769
|
+
import("tmcp"),
|
|
770
|
+
import("@tmcp/adapter-valibot"),
|
|
771
|
+
import("@tmcp/transport-http"),
|
|
772
|
+
import("@storybook/mcp"),
|
|
773
|
+
Promise.resolve().then(() => (init_storyInstructions(), storyInstructions_exports)),
|
|
774
|
+
Promise.resolve().then(() => (init_buildIndex(), buildIndex_exports)),
|
|
775
|
+
import("valibot"),
|
|
776
|
+
import("@storybook/react/preset")
|
|
777
|
+
]);
|
|
778
|
+
const manifestProvider = async (_request, manifestPath) => {
|
|
779
|
+
if (manifestPath.includes("docs.json")) {
|
|
780
|
+
throw new Error("Docs manifest not available in React Native Storybook");
|
|
781
|
+
}
|
|
782
|
+
const index = await buildIndex2({ configPath });
|
|
783
|
+
const entries = Object.values(index.entries);
|
|
784
|
+
const manifest = await experimental_manifests({}, { manifestEntries: entries });
|
|
785
|
+
return JSON.stringify(manifest.components);
|
|
786
|
+
};
|
|
787
|
+
const server = new McpServer(
|
|
788
|
+
{
|
|
789
|
+
name: "@storybook/react-native",
|
|
790
|
+
version: "1.0.0",
|
|
791
|
+
description: "Storybook React Native MCP server"
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
adapter: new ValibotJsonSchemaAdapter(),
|
|
795
|
+
capabilities: {
|
|
796
|
+
tools: { listChanged: true }
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
).withContext();
|
|
800
|
+
addListAllDocumentationTool(server);
|
|
801
|
+
addGetDocumentationTool(server);
|
|
802
|
+
addGetStoryDocumentationTool(server);
|
|
803
|
+
server.tool(
|
|
804
|
+
{
|
|
805
|
+
name: "get-storybook-story-instructions",
|
|
806
|
+
title: "React Native Storybook Story Instructions",
|
|
807
|
+
description: "Get instructions for writing React Native Storybook stories. Call this before creating or modifying story files (.stories.tsx, .stories.ts)."
|
|
808
|
+
},
|
|
809
|
+
async () => ({
|
|
810
|
+
content: [{ type: "text", text: storyInstructions2 }]
|
|
811
|
+
})
|
|
812
|
+
);
|
|
813
|
+
if (wss) {
|
|
814
|
+
const broadcastEvent = (event) => {
|
|
815
|
+
const message = JSON.stringify(event);
|
|
816
|
+
wss.clients.forEach((client) => {
|
|
817
|
+
if (client.readyState === 1) {
|
|
818
|
+
client.send(message);
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
};
|
|
822
|
+
server.tool(
|
|
823
|
+
{
|
|
824
|
+
name: "select-story",
|
|
825
|
+
title: "Select Story",
|
|
826
|
+
description: 'Select and display a story on the connected device. Use the story ID in the format "title--name" (e.g. "button--primary"). Use the list-all-documentation tool to discover available components and stories.',
|
|
827
|
+
schema: valibot.object({ storyId: valibot.string() })
|
|
828
|
+
},
|
|
829
|
+
async ({ storyId }) => {
|
|
830
|
+
try {
|
|
831
|
+
const index = await buildIndex2({ configPath });
|
|
832
|
+
if (!index.entries[storyId]) {
|
|
833
|
+
const availableIds = Object.keys(index.entries).slice(0, 10);
|
|
834
|
+
return {
|
|
835
|
+
content: [
|
|
836
|
+
{
|
|
837
|
+
type: "text",
|
|
838
|
+
text: `Story "${storyId}" not found. Available stories include: ${availableIds.join(", ")}` + (Object.keys(index.entries).length > 10 ? ", ..." : "")
|
|
839
|
+
}
|
|
840
|
+
],
|
|
841
|
+
isError: true
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
broadcastEvent({
|
|
845
|
+
type: "setCurrentStory",
|
|
846
|
+
args: [{ storyId, viewMode: "story" }]
|
|
847
|
+
});
|
|
848
|
+
const entry = index.entries[storyId];
|
|
849
|
+
return {
|
|
850
|
+
content: [
|
|
851
|
+
{
|
|
852
|
+
type: "text",
|
|
853
|
+
text: `Selected story "${entry.name}" (${entry.title}) on connected devices.`
|
|
854
|
+
}
|
|
855
|
+
]
|
|
856
|
+
};
|
|
857
|
+
} catch (error) {
|
|
858
|
+
return {
|
|
859
|
+
content: [
|
|
860
|
+
{
|
|
861
|
+
type: "text",
|
|
862
|
+
text: `Failed to select story: ${error instanceof Error ? error.message : String(error)}`
|
|
863
|
+
}
|
|
864
|
+
],
|
|
865
|
+
isError: true
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
const transport = new HttpTransport(server, { path: null });
|
|
872
|
+
handler = (req) => transport.respond(req, {
|
|
873
|
+
request: req,
|
|
874
|
+
manifestProvider
|
|
875
|
+
});
|
|
876
|
+
console.log("[Storybook] MCP server initialized");
|
|
877
|
+
} catch (error) {
|
|
878
|
+
initPromise = null;
|
|
879
|
+
console.error("[Storybook] Failed to initialize MCP server:", error);
|
|
880
|
+
throw error;
|
|
881
|
+
}
|
|
882
|
+
})();
|
|
883
|
+
await initPromise;
|
|
884
|
+
}
|
|
885
|
+
async function handleMcpRequest(req, res) {
|
|
886
|
+
try {
|
|
887
|
+
await init();
|
|
888
|
+
if (!handler) {
|
|
889
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
890
|
+
res.end(JSON.stringify({ error: "MCP handler not initialized" }));
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
const webRequest = await incomingMessageToWebRequest(req);
|
|
894
|
+
const webResponse = await handler(webRequest);
|
|
895
|
+
await webResponseToServerResponse(webResponse, res);
|
|
896
|
+
} catch (error) {
|
|
897
|
+
console.error("[Storybook] MCP request failed:", error);
|
|
898
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
899
|
+
res.end(JSON.stringify({ error: "MCP request failed" }));
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
function preInit() {
|
|
903
|
+
init().catch(
|
|
904
|
+
(e) => console.warn("[Storybook] MCP pre-initialization failed (will retry on first request):", e)
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
return { handleMcpRequest, preInit };
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// src/metro/selectStorySyncEndpoint.ts
|
|
911
|
+
var import_ws = require("ws");
|
|
912
|
+
var SELECT_STORY_SYNC_ROUTE = "/select-story-sync/";
|
|
913
|
+
var SELECT_STORY_SYNC_TIMEOUT_MS = 1e3;
|
|
914
|
+
var LAST_RENDERED_STORY_TIMEOUT_MS = 500;
|
|
915
|
+
function getRenderedStoryId(event) {
|
|
916
|
+
if (!event || typeof event !== "object") {
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
919
|
+
const { type, args } = event;
|
|
920
|
+
if (type !== "storyRendered" || !Array.isArray(args) || args.length === 0) {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
const [firstArg] = args;
|
|
924
|
+
if (typeof firstArg === "string") {
|
|
925
|
+
return firstArg;
|
|
926
|
+
}
|
|
927
|
+
if (firstArg && typeof firstArg === "object" && "storyId" in firstArg) {
|
|
928
|
+
const { storyId } = firstArg;
|
|
929
|
+
return typeof storyId === "string" ? storyId : null;
|
|
930
|
+
}
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
function parseStoryIdFromPath(pathname) {
|
|
934
|
+
const match = pathname.match(/^\/select-story-sync\/([^/]+)$/);
|
|
935
|
+
if (!match) {
|
|
936
|
+
return null;
|
|
937
|
+
}
|
|
938
|
+
try {
|
|
939
|
+
const storyId = decodeURIComponent(match[1]);
|
|
940
|
+
return storyId || null;
|
|
941
|
+
} catch {
|
|
942
|
+
return null;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
function createSelectStorySyncEndpoint(wss) {
|
|
946
|
+
const pendingStorySelections = /* @__PURE__ */ new Map();
|
|
947
|
+
const lastRenderedStoryIdByClient = /* @__PURE__ */ new Map();
|
|
948
|
+
const waitForStoryRender = (storyId, timeoutMs) => {
|
|
949
|
+
let cancelSelection = () => {
|
|
950
|
+
};
|
|
951
|
+
let resolveWait = () => {
|
|
952
|
+
};
|
|
953
|
+
const promise = new Promise((resolve4, reject) => {
|
|
954
|
+
resolveWait = resolve4;
|
|
955
|
+
let selections = pendingStorySelections.get(storyId);
|
|
956
|
+
if (!selections) {
|
|
957
|
+
selections = /* @__PURE__ */ new Set();
|
|
958
|
+
pendingStorySelections.set(storyId, selections);
|
|
959
|
+
}
|
|
960
|
+
const cleanup = () => {
|
|
961
|
+
clearTimeout(selection.timeout);
|
|
962
|
+
selections.delete(selection);
|
|
963
|
+
if (selections.size === 0) {
|
|
964
|
+
pendingStorySelections.delete(storyId);
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
const selection = {
|
|
968
|
+
resolve: () => {
|
|
969
|
+
if (selection.settled) {
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
selection.settled = true;
|
|
973
|
+
cleanup();
|
|
974
|
+
resolve4();
|
|
975
|
+
},
|
|
976
|
+
timeout: setTimeout(() => {
|
|
977
|
+
if (selection.settled) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
selection.settled = true;
|
|
981
|
+
cleanup();
|
|
982
|
+
reject(new Error(`Story "${storyId}" did not render in time`));
|
|
983
|
+
}, timeoutMs),
|
|
984
|
+
settled: false
|
|
985
|
+
};
|
|
986
|
+
cancelSelection = () => {
|
|
987
|
+
if (selection.settled) {
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
selection.settled = true;
|
|
991
|
+
cleanup();
|
|
992
|
+
resolveWait();
|
|
993
|
+
};
|
|
994
|
+
selections.add(selection);
|
|
995
|
+
});
|
|
996
|
+
return {
|
|
997
|
+
promise,
|
|
998
|
+
cancel: cancelSelection
|
|
999
|
+
};
|
|
1000
|
+
};
|
|
1001
|
+
const resolveStorySelection = (storyId) => {
|
|
1002
|
+
const selections = pendingStorySelections.get(storyId);
|
|
1003
|
+
if (!selections) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
[...selections].forEach((selection) => selection.resolve());
|
|
1007
|
+
};
|
|
1008
|
+
const handleRequest = async (pathname, res) => {
|
|
1009
|
+
const storyId = parseStoryIdFromPath(pathname);
|
|
1010
|
+
if (!storyId) {
|
|
1011
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1012
|
+
res.end(JSON.stringify({ success: false, error: "Invalid story id" }));
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
const waitForRender = waitForStoryRender(storyId, SELECT_STORY_SYNC_TIMEOUT_MS);
|
|
1016
|
+
const message = JSON.stringify({
|
|
1017
|
+
type: "setCurrentStory",
|
|
1018
|
+
args: [{ viewMode: "story", storyId }]
|
|
1019
|
+
});
|
|
1020
|
+
wss.clients.forEach((wsClient) => {
|
|
1021
|
+
if (wsClient.readyState === import_ws.WebSocket.OPEN) {
|
|
1022
|
+
wsClient.send(message);
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
try {
|
|
1026
|
+
const hasConnectedClientWithRenderedStory = [...wss.clients].some(
|
|
1027
|
+
(client) => client.readyState === import_ws.WebSocket.OPEN && lastRenderedStoryIdByClient.get(client) === storyId
|
|
1028
|
+
);
|
|
1029
|
+
if (hasConnectedClientWithRenderedStory) {
|
|
1030
|
+
const raceResult = await Promise.race([
|
|
1031
|
+
waitForRender.promise.then(() => "rendered"),
|
|
1032
|
+
new Promise((resolve4) => {
|
|
1033
|
+
setTimeout(() => resolve4("alreadyRendered"), LAST_RENDERED_STORY_TIMEOUT_MS);
|
|
1034
|
+
})
|
|
1035
|
+
]);
|
|
1036
|
+
if (raceResult === "alreadyRendered") {
|
|
1037
|
+
waitForRender.cancel();
|
|
1038
|
+
}
|
|
1039
|
+
} else {
|
|
1040
|
+
await waitForRender.promise;
|
|
1041
|
+
}
|
|
1042
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1043
|
+
res.end(JSON.stringify({ success: true, storyId }));
|
|
1044
|
+
} catch (error) {
|
|
1045
|
+
res.writeHead(408, { "Content-Type": "application/json" });
|
|
1046
|
+
res.end(
|
|
1047
|
+
JSON.stringify({
|
|
1048
|
+
success: false,
|
|
1049
|
+
storyId,
|
|
1050
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1051
|
+
})
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
const onSocketMessage = (event, ws) => {
|
|
1056
|
+
const renderedStoryId = getRenderedStoryId(event);
|
|
1057
|
+
if (renderedStoryId) {
|
|
1058
|
+
lastRenderedStoryIdByClient.set(ws, renderedStoryId);
|
|
1059
|
+
resolveStorySelection(renderedStoryId);
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
const onSocketClose = (ws) => {
|
|
1063
|
+
lastRenderedStoryIdByClient.delete(ws);
|
|
1064
|
+
};
|
|
1065
|
+
return {
|
|
1066
|
+
handleRequest,
|
|
1067
|
+
onSocketMessage,
|
|
1068
|
+
onSocketClose
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// src/metro/channelServer.ts
|
|
1073
|
+
function createChannelServer({
|
|
1074
|
+
port = 7007,
|
|
1075
|
+
host = void 0,
|
|
1076
|
+
configPath,
|
|
1077
|
+
experimental_mcp = false,
|
|
1078
|
+
websockets = true,
|
|
1079
|
+
secured = false,
|
|
1080
|
+
ssl
|
|
1081
|
+
}) {
|
|
1082
|
+
if (secured && (!ssl?.key || !ssl?.cert)) {
|
|
1083
|
+
throw new Error("[Storybook] Secure channel server requires both `ssl.key` and `ssl.cert`.");
|
|
1084
|
+
}
|
|
1085
|
+
const httpServer = secured ? (0, import_node_https.createServer)(ssl) : (0, import_node_http.createServer)();
|
|
1086
|
+
const wss = websockets ? new import_ws2.WebSocketServer({ server: httpServer }) : null;
|
|
1087
|
+
const mcpServer = experimental_mcp ? createMcpHandler(configPath, wss ?? void 0) : null;
|
|
1088
|
+
const selectStorySyncEndpoint = wss ? createSelectStorySyncEndpoint(wss) : null;
|
|
1089
|
+
httpServer.on("request", async (req, res) => {
|
|
1090
|
+
const protocol = "encrypted" in req.socket && req.socket.encrypted ? "https" : "http";
|
|
1091
|
+
const requestUrl = new URL(req.url ?? "/", `${protocol}://${req.headers.host ?? "localhost"}`);
|
|
1092
|
+
if (req.method === "OPTIONS") {
|
|
1093
|
+
res.writeHead(204);
|
|
1094
|
+
res.end();
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
if (req.method === "GET" && requestUrl.pathname === "/index.json") {
|
|
1098
|
+
try {
|
|
1099
|
+
const index = await buildIndex({ configPath });
|
|
1100
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1101
|
+
res.end(JSON.stringify(index));
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
console.error("Failed to build index:", error);
|
|
1104
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1105
|
+
res.end(JSON.stringify({ error: "Failed to build story index" }));
|
|
1106
|
+
}
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
if (req.method === "POST" && requestUrl.pathname === "/send-event") {
|
|
1110
|
+
if (!wss) {
|
|
1111
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1112
|
+
res.end(JSON.stringify({ success: false, error: "WebSockets are disabled" }));
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
let body = "";
|
|
1116
|
+
req.on("data", (chunk) => {
|
|
1117
|
+
body += chunk.toString();
|
|
1118
|
+
});
|
|
1119
|
+
req.on("end", () => {
|
|
1120
|
+
try {
|
|
1121
|
+
const json = JSON.parse(body);
|
|
1122
|
+
wss.clients.forEach((wsClient) => wsClient.send(JSON.stringify(json)));
|
|
1123
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1124
|
+
res.end(JSON.stringify({ success: true }));
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
console.error("Failed to parse event:", error);
|
|
1127
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1128
|
+
res.end(JSON.stringify({ success: false, error: "Invalid JSON" }));
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
if (req.method === "POST" && requestUrl.pathname.startsWith(SELECT_STORY_SYNC_ROUTE)) {
|
|
1134
|
+
if (!selectStorySyncEndpoint) {
|
|
1135
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1136
|
+
res.end(JSON.stringify({ success: false, error: "WebSockets are disabled" }));
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
await selectStorySyncEndpoint.handleRequest(requestUrl.pathname, res);
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
if (mcpServer && requestUrl.pathname === "/mcp" && (req.method === "POST" || req.method === "GET")) {
|
|
1143
|
+
await mcpServer.handleMcpRequest(req, res);
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1147
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
1148
|
+
});
|
|
1149
|
+
if (wss) {
|
|
1150
|
+
wss.on("error", () => {
|
|
1151
|
+
});
|
|
1152
|
+
const pingInterval = setInterval(function ping() {
|
|
1153
|
+
wss.clients.forEach(function each(client) {
|
|
1154
|
+
if (client.readyState === import_ws2.WebSocket.OPEN) {
|
|
1155
|
+
client.send(JSON.stringify({ type: "ping", args: [] }));
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
}, 1e4);
|
|
1159
|
+
pingInterval.unref?.();
|
|
1160
|
+
wss.on("connection", function connection(ws) {
|
|
1161
|
+
console.log("WebSocket connection established");
|
|
1162
|
+
ws.on("error", console.error);
|
|
1163
|
+
ws.on("message", function message(data) {
|
|
1164
|
+
try {
|
|
1165
|
+
const json = JSON.parse(data.toString());
|
|
1166
|
+
selectStorySyncEndpoint?.onSocketMessage(json, ws);
|
|
1167
|
+
const msg = JSON.stringify(json);
|
|
1168
|
+
wss.clients.forEach((wsClient) => {
|
|
1169
|
+
if (wsClient !== ws && wsClient.readyState === import_ws2.WebSocket.OPEN) {
|
|
1170
|
+
wsClient.send(msg);
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
1173
|
+
} catch (error) {
|
|
1174
|
+
console.error(error);
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
ws.on("close", () => {
|
|
1178
|
+
selectStorySyncEndpoint?.onSocketClose(ws);
|
|
1179
|
+
});
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
httpServer.on("error", (error) => {
|
|
1183
|
+
if (error.code === "EADDRINUSE") {
|
|
1184
|
+
console.warn(
|
|
1185
|
+
`[Storybook] Port ${port} is already in use. The channel server will not start. Another instance may already be running.`
|
|
1186
|
+
);
|
|
1187
|
+
} else {
|
|
1188
|
+
console.error(`[Storybook] Channel server error:`, error);
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1191
|
+
httpServer.listen(port, host, () => {
|
|
1192
|
+
const protocol = wss ? secured ? "WSS" : "WebSocket" : secured ? "HTTPS" : "HTTP";
|
|
1193
|
+
console.log(`${protocol} server listening on ${host ?? "localhost"}:${port}`);
|
|
1194
|
+
});
|
|
1195
|
+
mcpServer?.preInit();
|
|
1196
|
+
return wss;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// src/withStorybook.ts
|
|
1200
|
+
function envVariableToBoolean(value, defaultValue = false) {
|
|
1201
|
+
switch (value) {
|
|
1202
|
+
case "true":
|
|
1203
|
+
return true;
|
|
1204
|
+
case "false":
|
|
1205
|
+
return false;
|
|
1206
|
+
default:
|
|
1207
|
+
return !!defaultValue;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
function envVariableToString(value, defaultValue) {
|
|
1211
|
+
return value ?? defaultValue;
|
|
1212
|
+
}
|
|
1213
|
+
function envVariableToNumber(value, defaultValue) {
|
|
1214
|
+
const parsed = parseInt(value ?? "", 10);
|
|
1215
|
+
if (!isNaN(parsed)) {
|
|
1216
|
+
return parsed;
|
|
1217
|
+
}
|
|
1218
|
+
return defaultValue;
|
|
1219
|
+
}
|
|
1220
|
+
function isMetroConfig(config) {
|
|
1221
|
+
return config != null && typeof config === "object" && "transformer" in config;
|
|
1222
|
+
}
|
|
1223
|
+
function loadWebsocketEnvOverrides(websockets) {
|
|
1224
|
+
const envHost = envVariableToString(
|
|
1225
|
+
process.env.STORYBOOK_WS_HOST,
|
|
1226
|
+
websockets === "auto" ? void 0 : websockets?.host ?? void 0
|
|
1227
|
+
);
|
|
1228
|
+
const envPort = envVariableToNumber(
|
|
1229
|
+
process.env.STORYBOOK_WS_PORT,
|
|
1230
|
+
websockets === "auto" ? 7007 : websockets?.port ?? 7007
|
|
1231
|
+
);
|
|
1232
|
+
const envSecured = envVariableToBoolean(process.env.STORYBOOK_WS_SECURED);
|
|
1233
|
+
if (websockets === void 0 && !envHost) {
|
|
1234
|
+
return {
|
|
1235
|
+
host: void 0,
|
|
1236
|
+
port: void 0,
|
|
1237
|
+
secured: false
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
const config = websockets === "auto" || websockets === void 0 ? {} : { ...websockets };
|
|
1241
|
+
if (envHost) {
|
|
1242
|
+
config.host = envHost;
|
|
1243
|
+
}
|
|
1244
|
+
if (envPort) {
|
|
1245
|
+
config.port = envPort;
|
|
1246
|
+
}
|
|
1247
|
+
if (envSecured) {
|
|
1248
|
+
config.secured = true;
|
|
1249
|
+
}
|
|
1250
|
+
return config;
|
|
1251
|
+
}
|
|
1252
|
+
function withStorybook(config, options = {}) {
|
|
1253
|
+
const enabled = envVariableToBoolean(process.env.STORYBOOK_ENABLED, false);
|
|
1254
|
+
if (!enabled) {
|
|
1255
|
+
return config;
|
|
1256
|
+
}
|
|
1257
|
+
const server = envVariableToBoolean(process.env.STORYBOOK_SERVER, true);
|
|
1258
|
+
const liteMode = envVariableToBoolean(process.env.STORYBOOK_LITE_MODE, options.liteMode ?? false);
|
|
1259
|
+
const settings = { ...options };
|
|
1260
|
+
if (server) {
|
|
1261
|
+
settings.experimental_mcp = false;
|
|
1262
|
+
}
|
|
1263
|
+
if (liteMode) {
|
|
1264
|
+
settings.docTools = false;
|
|
1265
|
+
}
|
|
1266
|
+
const defaultConfigPath = path4.resolve(process.cwd(), "./.rnstorybook");
|
|
1267
|
+
const configPath = options.configPath || defaultConfigPath;
|
|
1268
|
+
const websockets = loadWebsocketEnvOverrides(options.websockets);
|
|
1269
|
+
const appEntryPoint = resolveEntryPoint();
|
|
1270
|
+
const storybookEntryPoint = resolveStorybookEntry(configPath);
|
|
1271
|
+
const swap = appEntryPoint && storybookEntryPoint ? { appEntryPoint, storybookEntryPoint } : void 0;
|
|
1272
|
+
const { useJs = false, docTools = true, experimental_mcp = false } = settings;
|
|
1273
|
+
if (server || experimental_mcp) {
|
|
1274
|
+
createChannelServer({
|
|
1275
|
+
port: websockets.port,
|
|
1276
|
+
host: websockets.host,
|
|
1277
|
+
configPath,
|
|
1278
|
+
experimental_mcp,
|
|
1279
|
+
websockets: Boolean(websockets.host),
|
|
1280
|
+
secured: websockets.secured,
|
|
1281
|
+
ssl: websockets.secured ? {
|
|
1282
|
+
key: websockets.key,
|
|
1283
|
+
cert: websockets.cert,
|
|
1284
|
+
ca: websockets.ca,
|
|
1285
|
+
passphrase: websockets.passphrase
|
|
1286
|
+
} : void 0
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
const host = websockets.host;
|
|
1290
|
+
(0, import_generate.generate)({
|
|
1291
|
+
configPath,
|
|
1292
|
+
useJs,
|
|
1293
|
+
docTools,
|
|
1294
|
+
...!!host ? { host, port: websockets.port, secured: !websockets.secured } : {}
|
|
1295
|
+
});
|
|
1296
|
+
if (isMetroConfig(config)) {
|
|
1297
|
+
return enhanceMetroConfig(config, { liteMode, swap });
|
|
1298
|
+
}
|
|
1299
|
+
return enhanceRepackConfig(config, { liteMode, swap });
|
|
1300
|
+
}
|
|
1301
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1302
|
+
0 && (module.exports = {
|
|
1303
|
+
withStorybook
|
|
1304
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storybook/react-native",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.4.0-canary-20260407095432",
|
|
4
4
|
"description": "A better way to develop React Native Components for your app",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"exports": {
|
|
25
25
|
".": "./dist/index.js",
|
|
26
|
+
"./withStorybook": "./dist/withStorybook.js",
|
|
26
27
|
"./metro/withStorybook": "./dist/metro/withStorybook.js",
|
|
27
28
|
"./repack/withStorybook": "./dist/repack/withStorybook.js",
|
|
28
29
|
"./metro-env": "./metro-env.d.ts",
|
|
@@ -45,9 +46,9 @@
|
|
|
45
46
|
"dependencies": {
|
|
46
47
|
"@storybook/mcp": "^0.6.1",
|
|
47
48
|
"@storybook/react": "^10.3.1",
|
|
48
|
-
"@storybook/react-native-theming": "
|
|
49
|
-
"@storybook/react-native-ui": "
|
|
50
|
-
"@storybook/react-native-ui-common": "
|
|
49
|
+
"@storybook/react-native-theming": "10.4.0-canary-20260407095432",
|
|
50
|
+
"@storybook/react-native-ui": "10.4.0-canary-20260407095432",
|
|
51
|
+
"@storybook/react-native-ui-common": "10.4.0-canary-20260407095432",
|
|
51
52
|
"@tmcp/adapter-valibot": "^0.1.4",
|
|
52
53
|
"@tmcp/transport-http": "^0.8.5",
|
|
53
54
|
"commander": "^14.0.2",
|