@storybook/react-native 10.1.11 → 10.2.0-beta.1
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/index.d.ts +10 -1
- package/dist/index.js +40 -51
- package/dist/metro/withStorybook.d.ts +1 -1
- package/dist/metro/withStorybook.js +261 -52
- package/dist/node.d.ts +40 -0
- package/dist/node.js +314 -0
- package/package.json +9 -7
- package/readme.md +29 -27
- package/scripts/common.js +13 -0
- package/scripts/generate.js +34 -6
- package/scripts/generate.test.js +82 -0
- package/scripts/generate.test.js.snapshot +25 -5
- package/scripts/handle-args.js +11 -1
package/dist/index.d.ts
CHANGED
|
@@ -76,6 +76,8 @@ declare class View {
|
|
|
76
76
|
storySpecifier: string;
|
|
77
77
|
viewMode: string;
|
|
78
78
|
}>;
|
|
79
|
+
_getHost: (params?: Partial<Params>) => any;
|
|
80
|
+
__getPort: (params?: Partial<Params>) => any;
|
|
79
81
|
_getServerChannel: (params?: Partial<Params>) => Channel;
|
|
80
82
|
createPreparedStoryMapping: () => Promise<void>;
|
|
81
83
|
getStorybookUI: (params?: Partial<Params>) => () => react_jsx_runtime.JSX.Element;
|
|
@@ -113,6 +115,13 @@ declare function updateView(viewInstance: View, annotations: any[], normalizedSt
|
|
|
113
115
|
req: any;
|
|
114
116
|
})[], options?: ReactNativeOptions): void;
|
|
115
117
|
|
|
118
|
+
declare const RN_STORYBOOK_STORAGE_KEY = "lastOpenedStory";
|
|
119
|
+
declare const RN_STORYBOOK_EVENTS: {
|
|
120
|
+
RN_GET_INDEX: string;
|
|
121
|
+
RN_GET_INDEX_RESPONSE: string;
|
|
122
|
+
};
|
|
123
|
+
declare const STORYBOOK_STORY_ID_PARAM = "STORYBOOK_STORY_ID";
|
|
124
|
+
|
|
116
125
|
interface StorybookConfig {
|
|
117
126
|
stories: StorybookConfig$1['stories'];
|
|
118
127
|
addons: Array<string | {
|
|
@@ -123,4 +132,4 @@ interface StorybookConfig {
|
|
|
123
132
|
framework?: '@storybook/react-native';
|
|
124
133
|
}
|
|
125
134
|
|
|
126
|
-
export { type InitialSelection, type Params, type Storage, type StorybookConfig, type ThemePartial, View, getProjectAnnotations, prepareStories, start, updateView };
|
|
135
|
+
export { type InitialSelection, type Params, RN_STORYBOOK_EVENTS, RN_STORYBOOK_STORAGE_KEY, STORYBOOK_STORY_ID_PARAM, type Storage, type StorybookConfig, type ThemePartial, View, getProjectAnnotations, prepareStories, start, updateView };
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
29
29
|
// src/index.ts
|
|
30
30
|
var index_exports = {};
|
|
31
31
|
__export(index_exports, {
|
|
32
|
+
RN_STORYBOOK_EVENTS: () => RN_STORYBOOK_EVENTS,
|
|
33
|
+
RN_STORYBOOK_STORAGE_KEY: () => RN_STORYBOOK_STORAGE_KEY,
|
|
34
|
+
STORYBOOK_STORY_ID_PARAM: () => STORYBOOK_STORY_ID_PARAM,
|
|
32
35
|
darkTheme: () => import_react_native_theming3.darkTheme,
|
|
33
36
|
getProjectAnnotations: () => getProjectAnnotations,
|
|
34
37
|
prepareStories: () => prepareStories,
|
|
@@ -857,54 +860,16 @@ var StoryView = ({ useWrapper = true }) => {
|
|
|
857
860
|
};
|
|
858
861
|
var StoryView_default = import_react3.default.memo(StoryView);
|
|
859
862
|
|
|
860
|
-
// src/
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
if (constants) {
|
|
868
|
-
var serverHost = constants.ServerHost || hostname;
|
|
869
|
-
return { hostname: serverHost.split(":")[0], passed: true };
|
|
870
|
-
}
|
|
871
|
-
return { hostname, passed: false };
|
|
872
|
-
}
|
|
873
|
-
function getConstants(config) {
|
|
874
|
-
return config && (config[0] === "AndroidConstants" || config[0] === "PlatformConstants");
|
|
875
|
-
}
|
|
876
|
-
function getByRNRequirePolyfill(hostname) {
|
|
877
|
-
var NativeModules;
|
|
878
|
-
var PlatformConstants;
|
|
879
|
-
var AndroidConstants;
|
|
880
|
-
if (typeof window === "undefined" || !window.__DEV__ || typeof window.require !== "function" || // RN >= 0.56
|
|
881
|
-
// TODO: Get NativeModules for RN >= 0.56
|
|
882
|
-
window.require.name === "metroRequire") {
|
|
883
|
-
return hostname;
|
|
884
|
-
}
|
|
885
|
-
NativeModules = window.require("NativeModules");
|
|
886
|
-
if (!NativeModules || !NativeModules.PlatformConstants && !NativeModules.AndroidConstants) {
|
|
887
|
-
return hostname;
|
|
888
|
-
}
|
|
889
|
-
PlatformConstants = NativeModules.PlatformConstants;
|
|
890
|
-
AndroidConstants = NativeModules.AndroidConstants;
|
|
891
|
-
var serverHost = (PlatformConstants ? PlatformConstants.ServerHost : AndroidConstants.ServerHost) || hostname;
|
|
892
|
-
return serverHost.split(":")[0];
|
|
893
|
-
}
|
|
894
|
-
function getHost(hostname) {
|
|
895
|
-
if (typeof __fbBatchedBridge !== "object" || hostname !== "localhost" && hostname !== "127.0.0.1") {
|
|
896
|
-
return hostname;
|
|
897
|
-
}
|
|
898
|
-
var result = getByRemoteConfig(hostname);
|
|
899
|
-
if (result.passed) {
|
|
900
|
-
return result.hostname;
|
|
901
|
-
}
|
|
902
|
-
return getByRNRequirePolyfill(hostname);
|
|
903
|
-
}
|
|
863
|
+
// src/constants.ts
|
|
864
|
+
var RN_STORYBOOK_STORAGE_KEY = "lastOpenedStory";
|
|
865
|
+
var RN_STORYBOOK_EVENTS = {
|
|
866
|
+
RN_GET_INDEX: "RN_GET_INDEX",
|
|
867
|
+
RN_GET_INDEX_RESPONSE: "RN_GET_INDEX_RESPONSE"
|
|
868
|
+
};
|
|
869
|
+
var STORYBOOK_STORY_ID_PARAM = "STORYBOOK_STORY_ID";
|
|
904
870
|
|
|
905
871
|
// src/View.tsx
|
|
906
872
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
907
|
-
var STORAGE_KEY = "lastOpenedStory";
|
|
908
873
|
var View3 = class {
|
|
909
874
|
_storyIndex;
|
|
910
875
|
_setStory = () => {
|
|
@@ -943,7 +908,7 @@ var View3 = class {
|
|
|
943
908
|
try {
|
|
944
909
|
let value = this._asyncStorageStoryId;
|
|
945
910
|
if (!value && this._storage != null) {
|
|
946
|
-
value = await this._storage.getItem(
|
|
911
|
+
value = await this._storage.getItem(RN_STORYBOOK_STORAGE_KEY);
|
|
947
912
|
this._asyncStorageStoryId = value;
|
|
948
913
|
}
|
|
949
914
|
const exists = value && this._storyIdExists(value);
|
|
@@ -955,9 +920,27 @@ var View3 = class {
|
|
|
955
920
|
}
|
|
956
921
|
return { storySpecifier: "*", viewMode: "story" };
|
|
957
922
|
};
|
|
923
|
+
_getHost = (params = {}) => {
|
|
924
|
+
if (params.host) {
|
|
925
|
+
return params.host;
|
|
926
|
+
}
|
|
927
|
+
if (globalThis.STORYBOOK_WEBSOCKET?.host) {
|
|
928
|
+
return globalThis.STORYBOOK_WEBSOCKET.host;
|
|
929
|
+
}
|
|
930
|
+
return import_react_native3.Platform.OS === "android" ? "10.0.2.2" : "localhost";
|
|
931
|
+
};
|
|
932
|
+
__getPort = (params = {}) => {
|
|
933
|
+
if (params.port) {
|
|
934
|
+
return params.port;
|
|
935
|
+
}
|
|
936
|
+
if (globalThis.STORYBOOK_WEBSOCKET?.port) {
|
|
937
|
+
return globalThis.STORYBOOK_WEBSOCKET.port;
|
|
938
|
+
}
|
|
939
|
+
return 7007;
|
|
940
|
+
};
|
|
958
941
|
_getServerChannel = (params = {}) => {
|
|
959
|
-
const host =
|
|
960
|
-
const port = `:${params
|
|
942
|
+
const host = this._getHost(params);
|
|
943
|
+
const port = `:${this.__getPort(params)}`;
|
|
961
944
|
const query = params.query || "";
|
|
962
945
|
const websocketType = params.secured ? "wss" : "ws";
|
|
963
946
|
const url = `${websocketType}://${host}${port}/${query}`;
|
|
@@ -1015,6 +998,9 @@ var View3 = class {
|
|
|
1015
998
|
channel.emit(import_core_events.CHANNEL_CREATED);
|
|
1016
999
|
this._preview.ready().then(() => this._preview.onStoryIndexChanged());
|
|
1017
1000
|
}
|
|
1001
|
+
this._channel.on(RN_STORYBOOK_EVENTS.RN_GET_INDEX, () => {
|
|
1002
|
+
this._channel.emit(RN_STORYBOOK_EVENTS.RN_GET_INDEX_RESPONSE, { index: this._storyIndex });
|
|
1003
|
+
});
|
|
1018
1004
|
import_manager_api.addons.loadAddons({
|
|
1019
1005
|
store: () => ({
|
|
1020
1006
|
fromId: (id) => {
|
|
@@ -1044,7 +1030,7 @@ var View3 = class {
|
|
|
1044
1030
|
const listener = import_react_native3.Linking.addEventListener("url", ({ url }) => {
|
|
1045
1031
|
if (typeof url === "string") {
|
|
1046
1032
|
const urlObj = new URL(url);
|
|
1047
|
-
const storyId = urlObj.searchParams.get(
|
|
1033
|
+
const storyId = urlObj.searchParams.get(STORYBOOK_STORY_ID_PARAM);
|
|
1048
1034
|
const hasStoryId = storyId && typeof storyId === "string";
|
|
1049
1035
|
const storyExists = hasStoryId && self._storyIdExists(storyId);
|
|
1050
1036
|
if (storyExists && self._ready) {
|
|
@@ -1069,7 +1055,7 @@ var View3 = class {
|
|
|
1069
1055
|
return import_react_native3.Linking.getInitialURL().then((url) => {
|
|
1070
1056
|
if (url && typeof url === "string") {
|
|
1071
1057
|
const urlObj = new URL(url);
|
|
1072
|
-
const storyId = urlObj.searchParams.get(
|
|
1058
|
+
const storyId = urlObj.searchParams.get(STORYBOOK_STORY_ID_PARAM);
|
|
1073
1059
|
const hasStoryId = storyId && typeof storyId === "string";
|
|
1074
1060
|
const storyExists = hasStoryId && self._storyIdExists(storyId);
|
|
1075
1061
|
if (hasStoryId && !storyExists) {
|
|
@@ -1112,7 +1098,7 @@ var View3 = class {
|
|
|
1112
1098
|
`);
|
|
1113
1099
|
}
|
|
1114
1100
|
if (shouldPersistSelection && !!self._storage) {
|
|
1115
|
-
self._storage.setItem(
|
|
1101
|
+
self._storage.setItem(RN_STORYBOOK_STORAGE_KEY, newStory.id).catch((e) => {
|
|
1116
1102
|
console.warn("storybook-log: error writing to async storage", e);
|
|
1117
1103
|
});
|
|
1118
1104
|
}
|
|
@@ -1412,6 +1398,9 @@ function updateView(viewInstance, annotations, normalizedStories, options) {
|
|
|
1412
1398
|
}
|
|
1413
1399
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1414
1400
|
0 && (module.exports = {
|
|
1401
|
+
RN_STORYBOOK_EVENTS,
|
|
1402
|
+
RN_STORYBOOK_STORAGE_KEY,
|
|
1403
|
+
STORYBOOK_STORY_ID_PARAM,
|
|
1415
1404
|
darkTheme,
|
|
1416
1405
|
getProjectAnnotations,
|
|
1417
1406
|
prepareStories,
|
|
@@ -24,7 +24,7 @@ interface WithStorybookOptions {
|
|
|
24
24
|
/**
|
|
25
25
|
* WebSocket configuration for syncing storybook instances or sending events to storybook.
|
|
26
26
|
*/
|
|
27
|
-
websockets?: WebsocketsOptions;
|
|
27
|
+
websockets?: WebsocketsOptions | 'auto';
|
|
28
28
|
/**
|
|
29
29
|
* Whether to use JavaScript files for Storybook configuration instead of TypeScript. Defaults to false.
|
|
30
30
|
*/
|
|
@@ -33,9 +33,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
33
33
|
var require_common = __commonJS({
|
|
34
34
|
"scripts/common.js"(exports2, module2) {
|
|
35
35
|
var { globToRegexp } = require("storybook/internal/common");
|
|
36
|
-
var
|
|
36
|
+
var path3 = require("path");
|
|
37
37
|
var fs = require("fs");
|
|
38
|
-
var
|
|
38
|
+
var cwd2 = process.cwd();
|
|
39
39
|
var toRequireContext = (specifier) => {
|
|
40
40
|
const { directory, files } = specifier;
|
|
41
41
|
const match = globToRegexp(`./${files}`);
|
|
@@ -48,14 +48,23 @@ var require_common = __commonJS({
|
|
|
48
48
|
var supportedExtensions = ["js", "jsx", "ts", "tsx", "cjs", "mjs"];
|
|
49
49
|
function getFilePathExtension({ configPath }, fileName) {
|
|
50
50
|
for (const ext of supportedExtensions) {
|
|
51
|
-
const filePath =
|
|
51
|
+
const filePath = path3.resolve(cwd2, configPath, `${fileName}.${ext}`);
|
|
52
52
|
if (fs.existsSync(filePath)) {
|
|
53
53
|
return ext;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
return null;
|
|
57
57
|
}
|
|
58
|
-
function
|
|
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) {
|
|
59
68
|
return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
60
69
|
}
|
|
61
70
|
function getPreviewExists({ configPath }) {
|
|
@@ -97,10 +106,11 @@ var require_common = __commonJS({
|
|
|
97
106
|
module2.exports = {
|
|
98
107
|
toRequireContext,
|
|
99
108
|
getFilePathExtension,
|
|
100
|
-
ensureRelativePathHasDot,
|
|
109
|
+
ensureRelativePathHasDot: ensureRelativePathHasDot2,
|
|
101
110
|
getPreviewExists,
|
|
102
111
|
resolveAddonFile,
|
|
103
|
-
getAddonName
|
|
112
|
+
getAddonName,
|
|
113
|
+
getFilePathWithExtension: getFilePathWithExtension2
|
|
104
114
|
};
|
|
105
115
|
}
|
|
106
116
|
});
|
|
@@ -142,25 +152,26 @@ var require_generate = __commonJS({
|
|
|
142
152
|
"scripts/generate.js"(exports2, module2) {
|
|
143
153
|
var {
|
|
144
154
|
toRequireContext,
|
|
145
|
-
ensureRelativePathHasDot,
|
|
155
|
+
ensureRelativePathHasDot: ensureRelativePathHasDot2,
|
|
146
156
|
getPreviewExists,
|
|
147
157
|
resolveAddonFile,
|
|
148
158
|
getAddonName
|
|
149
159
|
} = require_common();
|
|
150
|
-
var { normalizeStories, globToRegexp, loadMainConfig } = require("storybook/internal/common");
|
|
160
|
+
var { normalizeStories: normalizeStories2, globToRegexp, loadMainConfig: loadMainConfig2 } = require("storybook/internal/common");
|
|
151
161
|
var { interopRequireDefault } = require_require_interop();
|
|
152
162
|
var fs = require("fs");
|
|
153
|
-
var
|
|
154
|
-
var
|
|
155
|
-
var
|
|
163
|
+
var { networkInterfaces } = require("os");
|
|
164
|
+
var path3 = require("path");
|
|
165
|
+
var cwd2 = process.cwd();
|
|
166
|
+
var loadMain = async ({ configPath, cwd: cwd3 }) => {
|
|
156
167
|
try {
|
|
157
|
-
const main = await
|
|
168
|
+
const main = await loadMainConfig2({ configDir: configPath, cwd: cwd3 });
|
|
158
169
|
return main;
|
|
159
170
|
} catch {
|
|
160
171
|
console.error("Error loading main config, trying fallback");
|
|
161
172
|
}
|
|
162
|
-
const mainPathTs =
|
|
163
|
-
const mainPathJs =
|
|
173
|
+
const mainPathTs = path3.resolve(cwd3, configPath, `main.ts`);
|
|
174
|
+
const mainPathJs = path3.resolve(cwd3, configPath, `main.js`);
|
|
164
175
|
if (fs.existsSync(mainPathTs)) {
|
|
165
176
|
return interopRequireDefault(mainPathTs);
|
|
166
177
|
} else if (fs.existsSync(mainPathJs)) {
|
|
@@ -169,26 +180,40 @@ var require_generate = __commonJS({
|
|
|
169
180
|
throw new Error(`Main config file not found at ${mainPathTs} or ${mainPathJs}`);
|
|
170
181
|
}
|
|
171
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
|
+
}
|
|
172
195
|
async function generate2({
|
|
173
196
|
configPath,
|
|
174
|
-
/* absolute = false, */
|
|
175
197
|
useJs = false,
|
|
176
|
-
docTools = true
|
|
198
|
+
docTools = true,
|
|
199
|
+
host = void 0,
|
|
200
|
+
port = 7007
|
|
177
201
|
}) {
|
|
178
|
-
const
|
|
179
|
-
|
|
202
|
+
const channelHost = host === "auto" ? getLocalIPAddress() : host;
|
|
203
|
+
const storybookRequiresLocation = path3.resolve(
|
|
204
|
+
cwd2,
|
|
180
205
|
configPath,
|
|
181
206
|
`storybook.requires.${useJs ? "js" : "ts"}`
|
|
182
207
|
);
|
|
183
|
-
const main = await loadMain({ configPath, cwd });
|
|
184
|
-
const storiesSpecifiers =
|
|
208
|
+
const main = await loadMain({ configPath, cwd: cwd2 });
|
|
209
|
+
const storiesSpecifiers = normalizeStories2(main.stories, {
|
|
185
210
|
configDir: configPath,
|
|
186
|
-
workingDir:
|
|
211
|
+
workingDir: cwd2
|
|
187
212
|
});
|
|
188
213
|
const normalizedStories = storiesSpecifiers.map((specifier) => {
|
|
189
214
|
const reg = globToRegexp(`./${specifier.files}`);
|
|
190
215
|
const { path: p, recursive: r, match: m } = toRequireContext(specifier);
|
|
191
|
-
const pathToStory =
|
|
216
|
+
const pathToStory = ensureRelativePathHasDot2(path3.posix.relative(configPath, p));
|
|
192
217
|
return `{
|
|
193
218
|
titlePrefix: "${specifier.titlePrefix}",
|
|
194
219
|
directory: "${specifier.directory}",
|
|
@@ -249,6 +274,7 @@ var require_generate = __commonJS({
|
|
|
249
274
|
declare global {
|
|
250
275
|
var view: View;
|
|
251
276
|
var STORIES: typeof normalizedStories;
|
|
277
|
+
var STORYBOOK_WEBSOCKET: { host: string; port: number } | undefined;
|
|
252
278
|
}
|
|
253
279
|
`;
|
|
254
280
|
const fileContent = `/* do not change this file, it is auto generated by storybook. */
|
|
@@ -264,24 +290,25 @@ ${useJs ? "" : globalTypes}
|
|
|
264
290
|
|
|
265
291
|
const annotations = ${annotations};
|
|
266
292
|
|
|
267
|
-
|
|
293
|
+
globalThis.STORIES = normalizedStories;
|
|
294
|
+
${channelHost ? `globalThis.STORYBOOK_WEBSOCKET = { host: '${channelHost}', port: ${port ?? 7007} };` : ""}
|
|
268
295
|
|
|
269
296
|
${useJs ? "" : "// @ts-ignore"}
|
|
270
297
|
module?.hot?.accept?.();
|
|
271
298
|
|
|
272
299
|
${optionsVar}
|
|
273
300
|
|
|
274
|
-
if (!
|
|
275
|
-
|
|
301
|
+
if (!globalThis.view) {
|
|
302
|
+
globalThis.view = start({
|
|
276
303
|
annotations,
|
|
277
304
|
storyEntries: normalizedStories,
|
|
278
305
|
${options ? ` ${options},` : ""}
|
|
279
306
|
});
|
|
280
307
|
} else {
|
|
281
|
-
updateView(
|
|
308
|
+
updateView(globalThis.view, annotations, normalizedStories${options ? `, ${options}` : ""});
|
|
282
309
|
}
|
|
283
310
|
|
|
284
|
-
export const view${useJs ? "" : ": View"} =
|
|
311
|
+
export const view${useJs ? "" : ": View"} = globalThis.view;
|
|
285
312
|
`;
|
|
286
313
|
fs.writeFileSync(storybookRequiresLocation, fileContent, {
|
|
287
314
|
encoding: "utf8",
|
|
@@ -300,27 +327,213 @@ __export(withStorybook_exports, {
|
|
|
300
327
|
withStorybook: () => withStorybook
|
|
301
328
|
});
|
|
302
329
|
module.exports = __toCommonJS(withStorybook_exports);
|
|
303
|
-
var
|
|
330
|
+
var path2 = __toESM(require("path"));
|
|
304
331
|
var import_generate = __toESM(require_generate());
|
|
332
|
+
var import_common3 = require("storybook/internal/common");
|
|
333
|
+
var import_telemetry = require("storybook/internal/telemetry");
|
|
334
|
+
|
|
335
|
+
// src/metro/channelServer.ts
|
|
305
336
|
var import_ws = require("ws");
|
|
337
|
+
var import_node_http = require("http");
|
|
338
|
+
|
|
339
|
+
// src/metro/buildIndex.ts
|
|
306
340
|
var import_common = require("storybook/internal/common");
|
|
307
|
-
var
|
|
341
|
+
var import_node_fs = require("fs");
|
|
342
|
+
var import_glob = require("glob");
|
|
343
|
+
var import_path = __toESM(require("path"));
|
|
344
|
+
var import_csf_tools = require("storybook/internal/csf-tools");
|
|
345
|
+
var import_csf = require("storybook/internal/csf");
|
|
346
|
+
var import_preview_api = require("storybook/internal/preview-api");
|
|
347
|
+
var import_common2 = __toESM(require_common());
|
|
348
|
+
var cwd = process.cwd();
|
|
349
|
+
var makeTitle = (fileName, specifier, userTitle) => {
|
|
350
|
+
const title = (0, import_preview_api.userOrAutoTitleFromSpecifier)(fileName, specifier, userTitle);
|
|
351
|
+
if (title) {
|
|
352
|
+
return title.replace("./", "");
|
|
353
|
+
} else if (userTitle) {
|
|
354
|
+
return userTitle.replace("./", "");
|
|
355
|
+
} else {
|
|
356
|
+
console.error("Could not generate title!!");
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
function ensureRelativePathHasDot(relativePath) {
|
|
361
|
+
return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
362
|
+
}
|
|
363
|
+
async function buildIndex({ configPath }) {
|
|
364
|
+
const main = await (0, import_common.loadMainConfig)({ configDir: configPath, cwd });
|
|
365
|
+
if (!main.stories || !Array.isArray(main.stories)) {
|
|
366
|
+
throw new Error("No stories found");
|
|
367
|
+
}
|
|
368
|
+
const storiesSpecifiers = (0, import_common.normalizeStories)(main.stories, {
|
|
369
|
+
configDir: configPath,
|
|
370
|
+
workingDir: cwd
|
|
371
|
+
});
|
|
372
|
+
const specifierStoryPaths = storiesSpecifiers.map((specifier) => {
|
|
373
|
+
return (0, import_glob.sync)(specifier.files, {
|
|
374
|
+
cwd: import_path.default.resolve(process.cwd(), specifier.directory),
|
|
375
|
+
absolute: true,
|
|
376
|
+
// default to always ignore (exclude) anything in node_modules
|
|
377
|
+
ignore: ["**/node_modules"]
|
|
378
|
+
}).map((storyPath) => {
|
|
379
|
+
const normalizePathForWindows = (str) => import_path.default.sep === "\\" ? str.replace(/\\/g, "/") : str;
|
|
380
|
+
return normalizePathForWindows(storyPath);
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
const csfStories = specifierStoryPaths.reduce(
|
|
384
|
+
(acc, specifierStoryPathList, specifierIndex) => {
|
|
385
|
+
const paths = specifierStoryPathList.map((storyPath) => {
|
|
386
|
+
const code = (0, import_node_fs.readFileSync)(storyPath, { encoding: "utf-8" }).toString();
|
|
387
|
+
const relativePath = ensureRelativePathHasDot(import_path.default.posix.relative(cwd, storyPath));
|
|
388
|
+
return {
|
|
389
|
+
result: (0, import_csf_tools.loadCsf)(code, {
|
|
390
|
+
fileName: storyPath,
|
|
391
|
+
makeTitle: (userTitle) => makeTitle(relativePath, storiesSpecifiers[specifierIndex], userTitle)
|
|
392
|
+
}).parse(),
|
|
393
|
+
specifier: storiesSpecifiers[specifierIndex],
|
|
394
|
+
fileName: relativePath
|
|
395
|
+
};
|
|
396
|
+
});
|
|
397
|
+
return [...acc, ...paths];
|
|
398
|
+
},
|
|
399
|
+
new Array()
|
|
400
|
+
);
|
|
401
|
+
const index = {
|
|
402
|
+
v: 5,
|
|
403
|
+
entries: {}
|
|
404
|
+
};
|
|
405
|
+
for (const { result, specifier, fileName } of csfStories) {
|
|
406
|
+
const { meta, stories } = result;
|
|
407
|
+
if (stories && stories.length > 0) {
|
|
408
|
+
for (const story of stories) {
|
|
409
|
+
const id = (0, import_csf.toId)(meta.title, story.name);
|
|
410
|
+
index.entries[id] = {
|
|
411
|
+
type: "story",
|
|
412
|
+
subtype: "story",
|
|
413
|
+
id,
|
|
414
|
+
name: story.name,
|
|
415
|
+
title: meta.title,
|
|
416
|
+
importPath: `${specifier.directory}/${import_path.default.posix.relative(specifier.directory, fileName)}`,
|
|
417
|
+
tags: ["story"]
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
console.log(`No stories found for ${fileName}`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
const previewPath = (0, import_common2.getFilePathWithExtension)({ configPath }, "preview");
|
|
426
|
+
const previewSourceCode = (0, import_node_fs.readFileSync)(previewPath, { encoding: "utf-8" }).toString();
|
|
427
|
+
const storySort = (0, import_csf_tools.getStorySortParameter)(previewSourceCode);
|
|
428
|
+
const sortableStories = Object.values(index.entries);
|
|
429
|
+
(0, import_preview_api.sortStoriesV7)(
|
|
430
|
+
sortableStories,
|
|
431
|
+
storySort,
|
|
432
|
+
sortableStories.map((entry) => entry.importPath)
|
|
433
|
+
);
|
|
434
|
+
const sorted = sortableStories.reduce(
|
|
435
|
+
(acc, item) => {
|
|
436
|
+
acc[item.id] = item;
|
|
437
|
+
return acc;
|
|
438
|
+
},
|
|
439
|
+
{}
|
|
440
|
+
);
|
|
441
|
+
return { v: 5, entries: sorted };
|
|
442
|
+
} catch {
|
|
443
|
+
console.warn("Failed to sort stories, using unordered index");
|
|
444
|
+
return index;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// src/metro/channelServer.ts
|
|
449
|
+
function createChannelServer({
|
|
450
|
+
port = 7007,
|
|
451
|
+
host = void 0,
|
|
452
|
+
configPath
|
|
453
|
+
}) {
|
|
454
|
+
const httpServer = (0, import_node_http.createServer)(async (req, res) => {
|
|
455
|
+
if (req.method === "OPTIONS") {
|
|
456
|
+
res.writeHead(204);
|
|
457
|
+
res.end();
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
if (req.method === "GET" && req.url === "/index.json") {
|
|
461
|
+
try {
|
|
462
|
+
const index = await buildIndex({ configPath });
|
|
463
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
464
|
+
res.end(JSON.stringify(index));
|
|
465
|
+
} catch (error) {
|
|
466
|
+
console.error("Failed to build index:", error);
|
|
467
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
468
|
+
res.end(JSON.stringify({ error: "Failed to build story index" }));
|
|
469
|
+
}
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (req.method === "POST" && req.url === "/send-event") {
|
|
473
|
+
let body = "";
|
|
474
|
+
req.on("data", (chunk) => {
|
|
475
|
+
body += chunk.toString();
|
|
476
|
+
});
|
|
477
|
+
req.on("end", () => {
|
|
478
|
+
try {
|
|
479
|
+
const json = JSON.parse(body);
|
|
480
|
+
wss.clients.forEach((wsClient) => wsClient.send(JSON.stringify(json)));
|
|
481
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
482
|
+
res.end(JSON.stringify({ success: true }));
|
|
483
|
+
} catch (error) {
|
|
484
|
+
console.error("Failed to parse event:", error);
|
|
485
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
486
|
+
res.end(JSON.stringify({ success: false, error: "Invalid JSON" }));
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
492
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
493
|
+
});
|
|
494
|
+
const wss = new import_ws.WebSocketServer({ server: httpServer });
|
|
495
|
+
setInterval(function ping() {
|
|
496
|
+
wss.clients.forEach(function each(client) {
|
|
497
|
+
if (client.readyState === import_ws.WebSocket.OPEN) {
|
|
498
|
+
client.send(JSON.stringify({ type: "ping", args: [] }));
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
}, 1e4);
|
|
502
|
+
wss.on("connection", function connection(ws) {
|
|
503
|
+
console.log("WebSocket connection established");
|
|
504
|
+
ws.on("error", console.error);
|
|
505
|
+
ws.on("message", function message(data) {
|
|
506
|
+
try {
|
|
507
|
+
const json = JSON.parse(data.toString());
|
|
508
|
+
wss.clients.forEach((wsClient) => wsClient.send(JSON.stringify(json)));
|
|
509
|
+
} catch (error) {
|
|
510
|
+
console.error(error);
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
httpServer.listen(port, host, () => {
|
|
515
|
+
console.log(`WebSocket server listening on ${host ?? "localhost"}:${port}`);
|
|
516
|
+
});
|
|
517
|
+
return wss;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// src/metro/withStorybook.ts
|
|
308
521
|
function withStorybook(config, options = {
|
|
309
522
|
useJs: false,
|
|
310
523
|
enabled: true,
|
|
311
524
|
docTools: true,
|
|
312
525
|
liteMode: false,
|
|
313
|
-
configPath:
|
|
526
|
+
configPath: path2.resolve(process.cwd(), "./.rnstorybook")
|
|
314
527
|
}) {
|
|
315
528
|
const {
|
|
316
|
-
configPath =
|
|
529
|
+
configPath = path2.resolve(process.cwd(), "./.rnstorybook"),
|
|
317
530
|
websockets,
|
|
318
531
|
useJs = false,
|
|
319
532
|
enabled = true,
|
|
320
533
|
docTools = true,
|
|
321
534
|
liteMode = false
|
|
322
535
|
} = options;
|
|
323
|
-
const disableTelemetry = (0,
|
|
536
|
+
const disableTelemetry = (0, import_common3.optionalEnvToBoolean)(process.env.STORYBOOK_DISABLE_TELEMETRY);
|
|
324
537
|
if (!disableTelemetry && enabled) {
|
|
325
538
|
const event = process.env.NODE_ENV === "production" ? "build" : "dev";
|
|
326
539
|
(0, import_telemetry.telemetry)(event, {}).catch((e) => {
|
|
@@ -346,7 +559,7 @@ function withStorybook(config, options = {
|
|
|
346
559
|
const resolved = resolveFunction(context, moduleName, platform);
|
|
347
560
|
if (resolved.filePath?.includes?.(`${configPath}/index.tsx`)) {
|
|
348
561
|
return {
|
|
349
|
-
filePath:
|
|
562
|
+
filePath: path2.resolve(__dirname, "../stub.js"),
|
|
350
563
|
type: "sourceFile"
|
|
351
564
|
};
|
|
352
565
|
}
|
|
@@ -359,27 +572,23 @@ function withStorybook(config, options = {
|
|
|
359
572
|
};
|
|
360
573
|
}
|
|
361
574
|
if (websockets) {
|
|
362
|
-
const port = websockets.port ?? 7007;
|
|
363
|
-
const host = websockets
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
575
|
+
const port = websockets === "auto" ? 7007 : websockets.port ?? 7007;
|
|
576
|
+
const host = websockets === "auto" ? "auto" : websockets.host;
|
|
577
|
+
createChannelServer({ port, host: host === "auto" ? void 0 : host, configPath });
|
|
578
|
+
(0, import_generate.generate)({
|
|
579
|
+
configPath,
|
|
580
|
+
useJs,
|
|
581
|
+
docTools,
|
|
582
|
+
host,
|
|
583
|
+
port
|
|
584
|
+
});
|
|
585
|
+
} else {
|
|
586
|
+
(0, import_generate.generate)({
|
|
587
|
+
configPath,
|
|
588
|
+
useJs,
|
|
589
|
+
docTools
|
|
376
590
|
});
|
|
377
591
|
}
|
|
378
|
-
(0, import_generate.generate)({
|
|
379
|
-
configPath,
|
|
380
|
-
useJs,
|
|
381
|
-
docTools
|
|
382
|
-
});
|
|
383
592
|
return {
|
|
384
593
|
...config,
|
|
385
594
|
transformer: {
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { WebSocketServer } from 'ws';
|
|
2
|
+
import { StoryIndex } from 'storybook/internal/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Options for creating a channel server.
|
|
6
|
+
*/
|
|
7
|
+
interface ChannelServerOptions {
|
|
8
|
+
/**
|
|
9
|
+
* The port the server will listen on.
|
|
10
|
+
*/
|
|
11
|
+
port?: number;
|
|
12
|
+
/**
|
|
13
|
+
* The host the server will bind to.
|
|
14
|
+
*/
|
|
15
|
+
host?: string;
|
|
16
|
+
/**
|
|
17
|
+
* The path to the Storybook config folder.
|
|
18
|
+
*/
|
|
19
|
+
configPath: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Creates a channel server for syncing storybook instances and sending events.
|
|
23
|
+
* The server provides both WebSocket and REST endpoints:
|
|
24
|
+
* - WebSocket: broadcasts all received messages to all connected clients
|
|
25
|
+
* - POST /send-event: sends an event to all WebSocket clients
|
|
26
|
+
* - GET /index.json: returns the story index built from story files
|
|
27
|
+
*
|
|
28
|
+
* @param options - Configuration options for the channel server.
|
|
29
|
+
* @param options.port - The port to listen on.
|
|
30
|
+
* @param options.host - The host to bind to.
|
|
31
|
+
* @param options.configPath - The path to the Storybook config folder.
|
|
32
|
+
* @returns The created WebSocketServer instance.
|
|
33
|
+
*/
|
|
34
|
+
declare function createChannelServer({ port, host, configPath, }: ChannelServerOptions): WebSocketServer;
|
|
35
|
+
|
|
36
|
+
declare function buildIndex({ configPath }: {
|
|
37
|
+
configPath: string;
|
|
38
|
+
}): Promise<StoryIndex>;
|
|
39
|
+
|
|
40
|
+
export { buildIndex, createChannelServer };
|