@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
|
-
|
|
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.
|
|
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.
|
|
54
|
-
"@storybook/react-native-ui": "^10.2.2-alpha.
|
|
55
|
-
"@storybook/react-native-ui-common": "^10.2.2-alpha.
|
|
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": "
|
|
110
|
+
"gitHead": "b996f5156b78ae94b967434130a6af1bee2876a2"
|
|
110
111
|
}
|