@storybook/react-native 10.2.2-alpha.1 → 10.2.2-alpha.3

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.
@@ -557,7 +557,8 @@ function withStorybook(config, options = {
557
557
  };
558
558
  }
559
559
  const resolved = resolveFunction(context, moduleName, platform);
560
- if (resolved.filePath?.includes?.(`${configPath}/index.tsx`)) {
560
+ const configIndexRegex = new RegExp(`${configPath}/index\\.(tsx?|jsx?)$`);
561
+ if (resolved.filePath && configIndexRegex.test(resolved.filePath)) {
561
562
  return {
562
563
  filePath: path2.resolve(__dirname, "../stub.js"),
563
564
  type: "sourceFile"
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Minimal compiler types for webpack/rspack compatibility.
3
+ * We define these inline to avoid requiring @rspack/core or webpack as dependencies.
4
+ */
5
+ interface Compiler {
6
+ options: {
7
+ resolve: {
8
+ alias?: Record<string, string | false>;
9
+ };
10
+ };
11
+ hooks: {
12
+ beforeCompile: {
13
+ tapPromise: (name: string, fn: () => Promise<void>) => void;
14
+ };
15
+ };
16
+ webpack: {
17
+ NormalModuleReplacementPlugin: new (pattern: RegExp, fn: (resource: {
18
+ request?: string;
19
+ }) => void) => {
20
+ apply: (compiler: Compiler) => void;
21
+ };
22
+ };
23
+ }
24
+ /**
25
+ * Options for configuring WebSockets used for syncing storybook instances or sending events to storybook.
26
+ */
27
+ interface WebsocketsOptions {
28
+ /**
29
+ * The port WebSocket server will listen on. Defaults to 7007.
30
+ */
31
+ port?: number;
32
+ /**
33
+ * The host WebSocket server will bind to. Defaults to 'localhost'.
34
+ */
35
+ host?: string;
36
+ }
37
+ /**
38
+ * Options for configuring the Storybook Repack plugin.
39
+ */
40
+ interface StorybookPluginOptions {
41
+ /**
42
+ * The path to the Storybook config folder. Defaults to './.rnstorybook'.
43
+ */
44
+ configPath?: string;
45
+ /**
46
+ * If false, strips Storybook from the bundle. Defaults to true.
47
+ */
48
+ enabled?: boolean;
49
+ /**
50
+ * WebSocket configuration for syncing storybook instances or sending events to storybook.
51
+ * When set to 'auto', uses port 7007 and auto-detects the host LAN IP address.
52
+ */
53
+ websockets?: WebsocketsOptions | 'auto';
54
+ /**
55
+ * Whether to use JavaScript files for Storybook configuration instead of TypeScript.
56
+ * When true, generates storybook.requires.js instead of storybook.requires.ts.
57
+ * Defaults to false.
58
+ */
59
+ useJs?: boolean;
60
+ /**
61
+ * Whether to include doc tools in the storybook.requires file.
62
+ * Doc tools provide additional documentation features. Defaults to true.
63
+ */
64
+ docTools?: boolean;
65
+ /**
66
+ * Whether to use lite mode for the storybook. In lite mode, the default storybook UI
67
+ * is mocked out so you don't need to install all its dependencies like reanimated etc.
68
+ * Defaults to false.
69
+ */
70
+ liteMode?: boolean;
71
+ }
72
+ /**
73
+ * Repack/Rspack plugin for React Native Storybook.
74
+ *
75
+ * Provides equivalent functionality to the Metro {@link withStorybook} wrapper:
76
+ * - Auto-generates `storybook.requires.ts` before compilation
77
+ * - Starts a WebSocket channel server for remote control / syncing
78
+ * - Supports `enabled: false` to strip Storybook from the bundle
79
+ * - Supports `liteMode` to mock out the full Storybook UI
80
+ *
81
+ * @example
82
+ * ```javascript
83
+ * import { StorybookPlugin } from '@storybook/react-native/repack/withStorybook';
84
+ *
85
+ * // In your rspack.config.mjs plugins array:
86
+ * new StorybookPlugin({
87
+ * enabled: true,
88
+ * websockets: 'auto',
89
+ * })
90
+ * ```
91
+ *
92
+ * @example
93
+ * ```javascript
94
+ * // Disable Storybook in production builds:
95
+ * new StorybookPlugin({
96
+ * enabled: process.env.STORYBOOK_ENABLED !== 'false',
97
+ * websockets: 'auto',
98
+ * })
99
+ * ```
100
+ */
101
+ declare class StorybookPlugin {
102
+ private options;
103
+ private generated;
104
+ private serverStarted;
105
+ constructor(options?: StorybookPluginOptions);
106
+ apply(compiler: Compiler): void;
107
+ /**
108
+ * When enabled: generate storybook.requires, optionally start websocket server,
109
+ * and set up liteMode aliases.
110
+ */
111
+ private applyEnabled;
112
+ /**
113
+ * When disabled: redirect all Storybook imports to empty modules,
114
+ * and replace the config folder index with a stub component.
115
+ */
116
+ private applyDisabled;
117
+ }
118
+
119
+ export { StorybookPlugin, type StorybookPluginOptions };
@@ -0,0 +1,602 @@
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 __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+
32
+ // scripts/common.js
33
+ var require_common = __commonJS({
34
+ "scripts/common.js"(exports2, module2) {
35
+ var { globToRegexp } = require("storybook/internal/common");
36
+ var path3 = require("path");
37
+ var fs = require("fs");
38
+ var cwd2 = process.cwd();
39
+ var toRequireContext = (specifier) => {
40
+ const { directory, files } = specifier;
41
+ const match = globToRegexp(`./${files}`);
42
+ return {
43
+ path: directory,
44
+ recursive: files.includes("**") || files.split("/").length > 1,
45
+ match
46
+ };
47
+ };
48
+ var supportedExtensions = ["js", "jsx", "ts", "tsx", "cjs", "mjs"];
49
+ function getFilePathExtension({ configPath }, fileName) {
50
+ for (const ext of supportedExtensions) {
51
+ const filePath = path3.resolve(cwd2, configPath, `${fileName}.${ext}`);
52
+ if (fs.existsSync(filePath)) {
53
+ return ext;
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+ function getFilePathWithExtension2({ configPath }, fileName) {
59
+ for (const ext of supportedExtensions) {
60
+ const filePath = path3.resolve(cwd2, configPath, `${fileName}.${ext}`);
61
+ if (fs.existsSync(filePath)) {
62
+ return filePath;
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+ function ensureRelativePathHasDot2(relativePath) {
68
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
69
+ }
70
+ function getPreviewExists({ configPath }) {
71
+ return !!getFilePathExtension({ configPath }, "preview");
72
+ }
73
+ function resolveAddonFile(addon, file, extensions = ["js", "mjs", "ts"], configPath) {
74
+ if (!addon || typeof addon !== "string") return null;
75
+ try {
76
+ const basePath = `${addon}/${file}`;
77
+ require.resolve(basePath);
78
+ return basePath;
79
+ } catch (_error) {
80
+ }
81
+ for (const ext of extensions) {
82
+ try {
83
+ const filePath = `${addon}/${file}.${ext}`;
84
+ require.resolve(filePath);
85
+ return filePath;
86
+ } catch (_error) {
87
+ }
88
+ }
89
+ if (addon.startsWith("./") || addon.startsWith("../")) {
90
+ try {
91
+ const extension = getFilePathExtension({ configPath }, `${addon}/${file}`);
92
+ if (extension) {
93
+ return `${addon}/${file}`;
94
+ }
95
+ } catch (_error) {
96
+ }
97
+ }
98
+ return null;
99
+ }
100
+ function getAddonName(addon) {
101
+ if (typeof addon === "string") return addon;
102
+ if (typeof addon === "object" && addon.name && typeof addon.name === "string") return addon.name;
103
+ console.error("Invalid addon configuration", addon);
104
+ return null;
105
+ }
106
+ module2.exports = {
107
+ toRequireContext,
108
+ getFilePathExtension,
109
+ ensureRelativePathHasDot: ensureRelativePathHasDot2,
110
+ getPreviewExists,
111
+ resolveAddonFile,
112
+ getAddonName,
113
+ getFilePathWithExtension: getFilePathWithExtension2
114
+ };
115
+ }
116
+ });
117
+
118
+ // scripts/require-interop.js
119
+ var require_require_interop = __commonJS({
120
+ "scripts/require-interop.js"(exports2, module2) {
121
+ var registered = false;
122
+ function interopRequireDefault(filePath) {
123
+ const hasEsbuildBeenRegistered = !!require("module")._extensions[".ts"];
124
+ if (registered === false && !hasEsbuildBeenRegistered) {
125
+ const { register } = require("esbuild-register/dist/node");
126
+ registered = true;
127
+ register({
128
+ target: `node${process.version.slice(1)}`,
129
+ format: "cjs",
130
+ hookIgnoreNodeModules: true,
131
+ // Some frameworks, like Stylus, rely on the 'name' property of classes or functions
132
+ // https://github.com/storybookjs/storybook/issues/19049
133
+ keepNames: true,
134
+ tsconfigRaw: `{
135
+ "compilerOptions": {
136
+ "strict": false,
137
+ "skipLibCheck": true,
138
+ },
139
+ }`
140
+ });
141
+ }
142
+ const result = require(filePath);
143
+ const isES6DefaultExported = typeof result === "object" && result !== null && typeof result.default !== "undefined";
144
+ return isES6DefaultExported ? result.default : result;
145
+ }
146
+ module2.exports = { interopRequireDefault };
147
+ }
148
+ });
149
+
150
+ // scripts/generate.js
151
+ var require_generate = __commonJS({
152
+ "scripts/generate.js"(exports2, module2) {
153
+ var {
154
+ toRequireContext,
155
+ ensureRelativePathHasDot: ensureRelativePathHasDot2,
156
+ getPreviewExists,
157
+ resolveAddonFile,
158
+ getAddonName
159
+ } = require_common();
160
+ var { normalizeStories: normalizeStories2, globToRegexp, loadMainConfig: loadMainConfig2 } = require("storybook/internal/common");
161
+ var { interopRequireDefault } = require_require_interop();
162
+ var fs = require("fs");
163
+ var { networkInterfaces } = require("os");
164
+ var path3 = require("path");
165
+ var cwd2 = process.cwd();
166
+ var loadMain = async ({ configPath, cwd: cwd3 }) => {
167
+ try {
168
+ const main = await loadMainConfig2({ configDir: configPath, cwd: cwd3 });
169
+ return main;
170
+ } catch {
171
+ console.error("Error loading main config, trying fallback");
172
+ }
173
+ const mainPathTs = path3.resolve(cwd3, configPath, `main.ts`);
174
+ const mainPathJs = path3.resolve(cwd3, configPath, `main.js`);
175
+ if (fs.existsSync(mainPathTs)) {
176
+ return interopRequireDefault(mainPathTs);
177
+ } else if (fs.existsSync(mainPathJs)) {
178
+ return interopRequireDefault(mainPathJs);
179
+ } else {
180
+ throw new Error(`Main config file not found at ${mainPathTs} or ${mainPathJs}`);
181
+ }
182
+ };
183
+ function getLocalIPAddress() {
184
+ const nets = networkInterfaces();
185
+ for (const name of Object.keys(nets)) {
186
+ for (const net of nets[name]) {
187
+ const familyV4Value = typeof net.family === "string" ? "IPv4" : 4;
188
+ if (net.family === familyV4Value && !net.internal) {
189
+ return net.address;
190
+ }
191
+ }
192
+ }
193
+ return "0.0.0.0";
194
+ }
195
+ async function generate2({
196
+ configPath,
197
+ useJs = false,
198
+ docTools = true,
199
+ host = void 0,
200
+ port = 7007
201
+ }) {
202
+ const channelHost = host === "auto" ? getLocalIPAddress() : host;
203
+ const storybookRequiresLocation = path3.resolve(
204
+ cwd2,
205
+ configPath,
206
+ `storybook.requires.${useJs ? "js" : "ts"}`
207
+ );
208
+ const main = await loadMain({ configPath, cwd: cwd2 });
209
+ const storiesSpecifiers = normalizeStories2(main.stories, {
210
+ configDir: configPath,
211
+ workingDir: cwd2
212
+ });
213
+ const normalizedStories = storiesSpecifiers.map((specifier) => {
214
+ const reg = globToRegexp(`./${specifier.files}`);
215
+ const { path: p, recursive: r, match: m } = toRequireContext(specifier);
216
+ const pathToStory = ensureRelativePathHasDot2(path3.posix.relative(configPath, p));
217
+ return `{
218
+ titlePrefix: "${specifier.titlePrefix}",
219
+ directory: "${specifier.directory}",
220
+ files: "${specifier.files}",
221
+ importPathMatcher: /${reg.source}/,
222
+ ${useJs ? "" : "// @ts-ignore"}
223
+ req: require.context(
224
+ '${pathToStory}',
225
+ ${r},
226
+ ${m}
227
+ ),
228
+ }`;
229
+ });
230
+ const registeredAddons = [];
231
+ for (const addon of main.addons) {
232
+ const registerPath = resolveAddonFile(
233
+ getAddonName(addon),
234
+ "register",
235
+ ["js", "mjs", "jsx", "ts", "tsx"],
236
+ configPath
237
+ );
238
+ if (registerPath) {
239
+ registeredAddons.push(`import "${registerPath}";`);
240
+ }
241
+ }
242
+ const docToolsAnnotation = 'require("@storybook/react-native/preview")';
243
+ const enhancers = [];
244
+ if (docTools) {
245
+ enhancers.push(docToolsAnnotation);
246
+ }
247
+ for (const addon of main.addons) {
248
+ const previewPath = resolveAddonFile(
249
+ getAddonName(addon),
250
+ "preview",
251
+ ["js", "mjs", "jsx", "ts", "tsx"],
252
+ configPath
253
+ );
254
+ if (previewPath) {
255
+ enhancers.push(`require('${previewPath}')`);
256
+ continue;
257
+ }
258
+ }
259
+ let options = "";
260
+ let optionsVar = "";
261
+ const reactNativeOptions = main.reactNative;
262
+ if (reactNativeOptions && typeof reactNativeOptions === "object") {
263
+ optionsVar = `const options = ${JSON.stringify(reactNativeOptions, null, 2)}`;
264
+ options = "options";
265
+ }
266
+ const previewExists = getPreviewExists({ configPath });
267
+ if (previewExists) {
268
+ enhancers.unshift("require('./preview')");
269
+ }
270
+ const annotations = `[
271
+ ${enhancers.join(",\n ")}
272
+ ]`;
273
+ const globalTypes = `
274
+ declare global {
275
+ var view: View;
276
+ var STORIES: typeof normalizedStories;
277
+ var STORYBOOK_WEBSOCKET: { host: string; port: number } | undefined;
278
+ }
279
+ `;
280
+ const fileContent = `/* do not change this file, it is auto generated by storybook. */
281
+ import { start, updateView${useJs ? "" : ", View"} } from '@storybook/react-native';
282
+
283
+ ${registeredAddons.join("\n")}
284
+
285
+ const normalizedStories = [
286
+ ${normalizedStories.join(",\n ")}
287
+ ];
288
+
289
+ ${useJs ? "" : globalTypes}
290
+
291
+ const annotations = ${annotations};
292
+
293
+ globalThis.STORIES = normalizedStories;
294
+ ${channelHost ? `globalThis.STORYBOOK_WEBSOCKET = { host: '${channelHost}', port: ${port ?? 7007} };` : ""}
295
+
296
+ ${useJs ? "" : "// @ts-ignore"}
297
+ module?.hot?.accept?.();
298
+
299
+ ${optionsVar}
300
+
301
+ if (!globalThis.view) {
302
+ globalThis.view = start({
303
+ annotations,
304
+ storyEntries: normalizedStories,
305
+ ${options ? ` ${options},` : ""}
306
+ });
307
+ } else {
308
+ updateView(globalThis.view, annotations, normalizedStories${options ? `, ${options}` : ""});
309
+ }
310
+
311
+ export const view${useJs ? "" : ": View"} = globalThis.view;
312
+ `;
313
+ fs.writeFileSync(storybookRequiresLocation, fileContent, {
314
+ encoding: "utf8",
315
+ flag: "w"
316
+ });
317
+ }
318
+ module2.exports = {
319
+ generate: generate2
320
+ };
321
+ }
322
+ });
323
+
324
+ // src/repack/withStorybook.ts
325
+ var withStorybook_exports = {};
326
+ __export(withStorybook_exports, {
327
+ StorybookPlugin: () => StorybookPlugin
328
+ });
329
+ module.exports = __toCommonJS(withStorybook_exports);
330
+ var path2 = __toESM(require("path"));
331
+ var import_generate = __toESM(require_generate());
332
+
333
+ // src/metro/channelServer.ts
334
+ var import_ws = require("ws");
335
+ var import_node_http = require("http");
336
+
337
+ // src/metro/buildIndex.ts
338
+ var import_common = require("storybook/internal/common");
339
+ var import_node_fs = require("fs");
340
+ var import_glob = require("glob");
341
+ var import_path = __toESM(require("path"));
342
+ var import_csf_tools = require("storybook/internal/csf-tools");
343
+ var import_csf = require("storybook/internal/csf");
344
+ var import_preview_api = require("storybook/internal/preview-api");
345
+ var import_common2 = __toESM(require_common());
346
+ var cwd = process.cwd();
347
+ var makeTitle = (fileName, specifier, userTitle) => {
348
+ const title = (0, import_preview_api.userOrAutoTitleFromSpecifier)(fileName, specifier, userTitle);
349
+ if (title) {
350
+ return title.replace("./", "");
351
+ } else if (userTitle) {
352
+ return userTitle.replace("./", "");
353
+ } else {
354
+ console.error("Could not generate title!!");
355
+ process.exit(1);
356
+ }
357
+ };
358
+ function ensureRelativePathHasDot(relativePath) {
359
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
360
+ }
361
+ async function buildIndex({ configPath }) {
362
+ const main = await (0, import_common.loadMainConfig)({ configDir: configPath, cwd });
363
+ if (!main.stories || !Array.isArray(main.stories)) {
364
+ throw new Error("No stories found");
365
+ }
366
+ const storiesSpecifiers = (0, import_common.normalizeStories)(main.stories, {
367
+ configDir: configPath,
368
+ workingDir: cwd
369
+ });
370
+ const specifierStoryPaths = storiesSpecifiers.map((specifier) => {
371
+ return (0, import_glob.sync)(specifier.files, {
372
+ cwd: import_path.default.resolve(process.cwd(), specifier.directory),
373
+ absolute: true,
374
+ // default to always ignore (exclude) anything in node_modules
375
+ ignore: ["**/node_modules"]
376
+ }).map((storyPath) => {
377
+ const normalizePathForWindows = (str) => import_path.default.sep === "\\" ? str.replace(/\\/g, "/") : str;
378
+ return normalizePathForWindows(storyPath);
379
+ });
380
+ });
381
+ const csfStories = specifierStoryPaths.reduce(
382
+ (acc, specifierStoryPathList, specifierIndex) => {
383
+ const paths = specifierStoryPathList.map((storyPath) => {
384
+ const code = (0, import_node_fs.readFileSync)(storyPath, { encoding: "utf-8" }).toString();
385
+ const relativePath = ensureRelativePathHasDot(import_path.default.posix.relative(cwd, storyPath));
386
+ return {
387
+ result: (0, import_csf_tools.loadCsf)(code, {
388
+ fileName: storyPath,
389
+ makeTitle: (userTitle) => makeTitle(relativePath, storiesSpecifiers[specifierIndex], userTitle)
390
+ }).parse(),
391
+ specifier: storiesSpecifiers[specifierIndex],
392
+ fileName: relativePath
393
+ };
394
+ });
395
+ return [...acc, ...paths];
396
+ },
397
+ new Array()
398
+ );
399
+ const index = {
400
+ v: 5,
401
+ entries: {}
402
+ };
403
+ for (const { result, specifier, fileName } of csfStories) {
404
+ const { meta, stories } = result;
405
+ if (stories && stories.length > 0) {
406
+ for (const story of stories) {
407
+ const id = (0, import_csf.toId)(meta.title, story.name);
408
+ index.entries[id] = {
409
+ type: "story",
410
+ subtype: "story",
411
+ id,
412
+ name: story.name,
413
+ title: meta.title,
414
+ importPath: `${specifier.directory}/${import_path.default.posix.relative(specifier.directory, fileName)}`,
415
+ tags: ["story"]
416
+ };
417
+ }
418
+ } else {
419
+ console.log(`No stories found for ${fileName}`);
420
+ }
421
+ }
422
+ try {
423
+ const previewPath = (0, import_common2.getFilePathWithExtension)({ configPath }, "preview");
424
+ const previewSourceCode = (0, import_node_fs.readFileSync)(previewPath, { encoding: "utf-8" }).toString();
425
+ const storySort = (0, import_csf_tools.getStorySortParameter)(previewSourceCode);
426
+ const sortableStories = Object.values(index.entries);
427
+ (0, import_preview_api.sortStoriesV7)(
428
+ sortableStories,
429
+ storySort,
430
+ sortableStories.map((entry) => entry.importPath)
431
+ );
432
+ const sorted = sortableStories.reduce(
433
+ (acc, item) => {
434
+ acc[item.id] = item;
435
+ return acc;
436
+ },
437
+ {}
438
+ );
439
+ return { v: 5, entries: sorted };
440
+ } catch {
441
+ console.warn("Failed to sort stories, using unordered index");
442
+ return index;
443
+ }
444
+ }
445
+
446
+ // src/metro/channelServer.ts
447
+ function createChannelServer({
448
+ port = 7007,
449
+ host = void 0,
450
+ configPath
451
+ }) {
452
+ const httpServer = (0, import_node_http.createServer)(async (req, res) => {
453
+ if (req.method === "OPTIONS") {
454
+ res.writeHead(204);
455
+ res.end();
456
+ return;
457
+ }
458
+ if (req.method === "GET" && req.url === "/index.json") {
459
+ try {
460
+ const index = await buildIndex({ configPath });
461
+ res.writeHead(200, { "Content-Type": "application/json" });
462
+ res.end(JSON.stringify(index));
463
+ } catch (error) {
464
+ console.error("Failed to build index:", error);
465
+ res.writeHead(500, { "Content-Type": "application/json" });
466
+ res.end(JSON.stringify({ error: "Failed to build story index" }));
467
+ }
468
+ return;
469
+ }
470
+ if (req.method === "POST" && req.url === "/send-event") {
471
+ let body = "";
472
+ req.on("data", (chunk) => {
473
+ body += chunk.toString();
474
+ });
475
+ req.on("end", () => {
476
+ try {
477
+ const json = JSON.parse(body);
478
+ wss.clients.forEach((wsClient) => wsClient.send(JSON.stringify(json)));
479
+ res.writeHead(200, { "Content-Type": "application/json" });
480
+ res.end(JSON.stringify({ success: true }));
481
+ } catch (error) {
482
+ console.error("Failed to parse event:", error);
483
+ res.writeHead(400, { "Content-Type": "application/json" });
484
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON" }));
485
+ }
486
+ });
487
+ return;
488
+ }
489
+ res.writeHead(404, { "Content-Type": "application/json" });
490
+ res.end(JSON.stringify({ error: "Not found" }));
491
+ });
492
+ const wss = new import_ws.WebSocketServer({ server: httpServer });
493
+ setInterval(function ping() {
494
+ wss.clients.forEach(function each(client) {
495
+ if (client.readyState === import_ws.WebSocket.OPEN) {
496
+ client.send(JSON.stringify({ type: "ping", args: [] }));
497
+ }
498
+ });
499
+ }, 1e4);
500
+ wss.on("connection", function connection(ws) {
501
+ console.log("WebSocket connection established");
502
+ ws.on("error", console.error);
503
+ ws.on("message", function message(data) {
504
+ try {
505
+ const json = JSON.parse(data.toString());
506
+ wss.clients.forEach((wsClient) => wsClient.send(JSON.stringify(json)));
507
+ } catch (error) {
508
+ console.error(error);
509
+ }
510
+ });
511
+ });
512
+ httpServer.listen(port, host, () => {
513
+ console.log(`WebSocket server listening on ${host ?? "localhost"}:${port}`);
514
+ });
515
+ return wss;
516
+ }
517
+
518
+ // src/repack/withStorybook.ts
519
+ var StorybookPlugin = class {
520
+ options;
521
+ generated = false;
522
+ serverStarted = false;
523
+ constructor(options = {}) {
524
+ this.options = {
525
+ configPath: path2.resolve(process.cwd(), "./.rnstorybook"),
526
+ enabled: true,
527
+ useJs: false,
528
+ docTools: true,
529
+ liteMode: false,
530
+ ...options
531
+ };
532
+ }
533
+ apply(compiler) {
534
+ const { configPath, enabled, websockets, useJs, docTools, liteMode } = this.options;
535
+ if (!enabled) {
536
+ this.applyDisabled(compiler, configPath);
537
+ return;
538
+ }
539
+ this.applyEnabled(compiler, { configPath, websockets, useJs, docTools, liteMode });
540
+ }
541
+ /**
542
+ * When enabled: generate storybook.requires, optionally start websocket server,
543
+ * and set up liteMode aliases.
544
+ */
545
+ applyEnabled(compiler, {
546
+ configPath,
547
+ websockets,
548
+ useJs,
549
+ docTools,
550
+ liteMode
551
+ }) {
552
+ const port = websockets === "auto" ? 7007 : websockets?.port ?? 7007;
553
+ const host = websockets === "auto" ? "auto" : websockets?.host;
554
+ if (websockets && !this.serverStarted) {
555
+ this.serverStarted = true;
556
+ createChannelServer({
557
+ port,
558
+ host: host === "auto" ? void 0 : host,
559
+ configPath
560
+ });
561
+ }
562
+ compiler.hooks.beforeCompile.tapPromise("StorybookPlugin", async () => {
563
+ if (this.generated) return;
564
+ this.generated = true;
565
+ await (0, import_generate.generate)({
566
+ configPath,
567
+ useJs,
568
+ docTools,
569
+ ...websockets ? { host, port } : {}
570
+ });
571
+ console.log("[StorybookPlugin] Generated storybook.requires");
572
+ });
573
+ if (liteMode) {
574
+ const alias = compiler.options.resolve.alias ?? {};
575
+ alias["@storybook/react-native-ui$"] = false;
576
+ compiler.options.resolve.alias = alias;
577
+ }
578
+ }
579
+ /**
580
+ * When disabled: redirect all Storybook imports to empty modules,
581
+ * and replace the config folder index with a stub component.
582
+ */
583
+ applyDisabled(compiler, configPath) {
584
+ const stubPath = require.resolve("@storybook/react-native/stub");
585
+ const normalizedConfigPath = path2.resolve(configPath);
586
+ new compiler.webpack.NormalModuleReplacementPlugin(/./, (resource) => {
587
+ const request = resource.request;
588
+ if (!request) return;
589
+ if (request.startsWith("@storybook") || request.startsWith("storybook")) {
590
+ resource.request = stubPath;
591
+ return;
592
+ }
593
+ }).apply(compiler);
594
+ const alias = compiler.options.resolve.alias ?? {};
595
+ alias[normalizedConfigPath] = stubPath;
596
+ compiler.options.resolve.alias = alias;
597
+ }
598
+ };
599
+ // Annotate the CommonJS export names for ESM import in node:
600
+ 0 && (module.exports = {
601
+ StorybookPlugin
602
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storybook/react-native",
3
- "version": "10.2.2-alpha.1",
3
+ "version": "10.2.2-alpha.3",
4
4
  "description": "A better way to develop React Native Components for your app",
5
5
  "keywords": [
6
6
  "react",
@@ -24,6 +24,7 @@
24
24
  "exports": {
25
25
  ".": "./dist/index.js",
26
26
  "./metro/withStorybook": "./dist/metro/withStorybook.js",
27
+ "./repack/withStorybook": "./dist/repack/withStorybook.js",
27
28
  "./node": "./dist/node.js",
28
29
  "./preview": "./dist/preview.js",
29
30
  "./scripts/generate": "./scripts/generate.js",
@@ -50,9 +51,9 @@
50
51
  },
51
52
  "dependencies": {
52
53
  "@storybook/react": "^10.2.2",
53
- "@storybook/react-native-theming": "^10.2.2-alpha.1",
54
- "@storybook/react-native-ui": "^10.2.2-alpha.1",
55
- "@storybook/react-native-ui-common": "^10.2.2-alpha.1",
54
+ "@storybook/react-native-theming": "^10.2.2-alpha.3",
55
+ "@storybook/react-native-ui": "^10.2.2-alpha.3",
56
+ "@storybook/react-native-ui-common": "^10.2.2-alpha.3",
56
57
  "commander": "^14.0.2",
57
58
  "dedent": "^1.7.0",
58
59
  "deepmerge": "^4.3.1",
@@ -106,5 +107,5 @@
106
107
  "publishConfig": {
107
108
  "access": "public"
108
109
  },
109
- "gitHead": "900db1ac2a226eb8206cc89b7142d1ea50272a9e"
110
+ "gitHead": "b996f5156b78ae94b967434130a6af1bee2876a2"
110
111
  }