Skip to content

Commit 9ce96d0

Browse files
authored
fix: hot reloading (#561)
* fix: hot reloading * fix: stupid warning
1 parent c1612f3 commit 9ce96d0

6 files changed

Lines changed: 235 additions & 94 deletions

File tree

examples/expo-example/.storybook/storybook.requires.ts

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
/* do not change this file, it is auto generated by storybook. */
22

3-
import { start } from "@storybook/react-native";
3+
import {
4+
start,
5+
prepareStories,
6+
getProjectAnnotations,
7+
} from "@storybook/react-native";
48

59
import "@storybook/addon-ondevice-notes/register";
610
import "@storybook/addon-ondevice-controls/register";
@@ -37,14 +41,37 @@ const normalizedStories = [
3741
},
3842
];
3943

40-
// @ts-ignore
44+
declare global {
45+
var view: ReturnType<typeof start>;
46+
var STORIES: typeof normalizedStories;
47+
}
48+
49+
const annotations = [
50+
require("./preview"),
51+
require("@storybook/react-native/dist/preview"),
52+
require("@storybook/addon-actions/preview"),
53+
];
54+
4155
global.STORIES = normalizedStories;
4256

43-
export const view = start({
44-
annotations: [
45-
require("./preview"),
46-
require("@storybook/react-native/dist/preview"),
47-
require("@storybook/addon-actions/preview"),
48-
],
49-
storyEntries: normalizedStories,
50-
});
57+
// @ts-ignore
58+
module?.hot?.accept?.();
59+
60+
if (!global.view) {
61+
global.view = start({
62+
annotations,
63+
storyEntries: normalizedStories,
64+
});
65+
} else {
66+
const { importMap } = prepareStories({ storyEntries: normalizedStories });
67+
68+
global.view._preview.onStoriesChanged({
69+
importFn: async (importPath: string) => importMap[importPath],
70+
});
71+
72+
global.view._preview.onGetProjectAnnotationsChanged({
73+
getProjectAnnotations: getProjectAnnotations(global.view, annotations),
74+
});
75+
}
76+
77+
export const view = global.view;

examples/expo-example/components/ControlExamples/Color/Color.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React from 'react';
2-
import { ColorValue, StyleSheet, Text, View } from 'react-native';
2+
import { StyleSheet, Text, View } from 'react-native';
33

44
interface ButtonProps {
5-
color: ColorValue;
5+
color: string;
66
}
77

88
const styles = StyleSheet.create({

packages/react-native/scripts/__snapshots__/generate.test.js.snap

Lines changed: 136 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
exports[`loader writeRequires when there are different file extensions writes the story imports 1`] = `
44
"
55
/* do not change this file, it is auto generated by storybook. */
6-
7-
import { start } from '@storybook/react-native';
6+
7+
import { start, prepareStories, getProjectAnnotations } from '@storybook/react-native';
88
99
import "@storybook/addon-ondevice-notes/register";
1010
import "@storybook/addon-ondevice-controls/register";
@@ -18,23 +18,48 @@ import "@storybook/addon-ondevice-actions/register";
1818
importPathMatcher: /^\\.[\\\\/](?:FakeStory\\.stories\\.tsx)$/,
1919
// @ts-ignore
2020
req: require.context('./', false, /^\\.[\\\\/](?:FakeStory\\.stories\\.tsx)$/)
21-
}]
21+
}];
2222
23-
// @ts-ignore
24-
global.STORIES = normalizedStories;
23+
24+
declare global {
25+
var view: ReturnType<typeof start>;
26+
var STORIES: typeof normalizedStories;
27+
}
28+
29+
30+
const annotations = [require('./preview'),require("@storybook/react-native/dist/preview"), require('@storybook/addon-actions/preview')];
2531
26-
export const view = start({
27-
annotations: [require('./preview'),require("@storybook/react-native/dist/preview"), require('@storybook/addon-actions/preview')],
28-
storyEntries: normalizedStories
29-
});
32+
global.STORIES = normalizedStories;
33+
34+
// @ts-ignore
35+
module?.hot?.accept?.();
36+
37+
if (!global.view) {
38+
global.view = start({
39+
annotations,
40+
storyEntries: normalizedStories
41+
});
42+
} else {
43+
const { importMap } = prepareStories({ storyEntries: normalizedStories });
44+
45+
global.view._preview.onStoriesChanged({
46+
importFn: async (importPath: string) => importMap[importPath],
47+
});
48+
49+
global.view._preview.onGetProjectAnnotationsChanged({
50+
getProjectAnnotations: getProjectAnnotations(global.view, annotations),
51+
});
52+
}
53+
54+
export const view = global.view;
3055
"
3156
`;
3257

3358
exports[`loader writeRequires when there is a configuration object writes the story imports 1`] = `
3459
"
3560
/* do not change this file, it is auto generated by storybook. */
36-
37-
import { start } from '@storybook/react-native';
61+
62+
import { start, prepareStories, getProjectAnnotations } from '@storybook/react-native';
3863
3964
import "@storybook/addon-ondevice-notes/register";
4065
import "@storybook/addon-ondevice-controls/register";
@@ -48,23 +73,48 @@ import "@storybook/addon-ondevice-actions/register";
4873
importPathMatcher: /^\\.(?:(?:^|\\/|(?:(?:(?!(?:^|\\/)\\.).)*?)\\/)(?!\\.)(?=.)[^/]*?\\.stories\\.tsx)$/,
4974
// @ts-ignore
5075
req: require.context('./components', true, /^\\.(?:(?:^|\\/|(?:(?:(?!(?:^|\\/)\\.).)*?)\\/)(?!\\.)(?=.)[^/]*?\\.stories\\.tsx)$/)
51-
}]
76+
}];
5277
53-
// @ts-ignore
54-
global.STORIES = normalizedStories;
78+
79+
declare global {
80+
var view: ReturnType<typeof start>;
81+
var STORIES: typeof normalizedStories;
82+
}
83+
84+
85+
const annotations = [require('./preview'),require("@storybook/react-native/dist/preview"), require('@storybook/addon-actions/preview')];
5586
56-
export const view = start({
57-
annotations: [require('./preview'),require("@storybook/react-native/dist/preview"), require('@storybook/addon-actions/preview')],
58-
storyEntries: normalizedStories
59-
});
87+
global.STORIES = normalizedStories;
88+
89+
// @ts-ignore
90+
module?.hot?.accept?.();
91+
92+
if (!global.view) {
93+
global.view = start({
94+
annotations,
95+
storyEntries: normalizedStories
96+
});
97+
} else {
98+
const { importMap } = prepareStories({ storyEntries: normalizedStories });
99+
100+
global.view._preview.onStoriesChanged({
101+
importFn: async (importPath: string) => importMap[importPath],
102+
});
103+
104+
global.view._preview.onGetProjectAnnotationsChanged({
105+
getProjectAnnotations: getProjectAnnotations(global.view, annotations),
106+
});
107+
}
108+
109+
export const view = global.view;
60110
"
61111
`;
62112

63113
exports[`loader writeRequires when there is a story glob writes the story imports 1`] = `
64114
"
65115
/* do not change this file, it is auto generated by storybook. */
66-
67-
import { start } from '@storybook/react-native';
116+
117+
import { start, prepareStories, getProjectAnnotations } from '@storybook/react-native';
68118
69119
import "@storybook/addon-ondevice-notes/register";
70120
import "@storybook/addon-ondevice-controls/register";
@@ -78,23 +128,48 @@ import "@storybook/addon-ondevice-actions/register";
78128
importPathMatcher: /^\\.[\\\\/](?:FakeStory\\.stories\\.tsx)$/,
79129
// @ts-ignore
80130
req: require.context('./', false, /^\\.[\\\\/](?:FakeStory\\.stories\\.tsx)$/)
81-
}]
131+
}];
82132
83-
// @ts-ignore
84-
global.STORIES = normalizedStories;
133+
134+
declare global {
135+
var view: ReturnType<typeof start>;
136+
var STORIES: typeof normalizedStories;
137+
}
138+
139+
140+
const annotations = [require('./preview'),require("@storybook/react-native/dist/preview"), require('@storybook/addon-actions/preview')];
85141
86-
export const view = start({
87-
annotations: [require('./preview'),require("@storybook/react-native/dist/preview"), require('@storybook/addon-actions/preview')],
88-
storyEntries: normalizedStories
89-
});
142+
global.STORIES = normalizedStories;
143+
144+
// @ts-ignore
145+
module?.hot?.accept?.();
146+
147+
if (!global.view) {
148+
global.view = start({
149+
annotations,
150+
storyEntries: normalizedStories
151+
});
152+
} else {
153+
const { importMap } = prepareStories({ storyEntries: normalizedStories });
154+
155+
global.view._preview.onStoriesChanged({
156+
importFn: async (importPath: string) => importMap[importPath],
157+
});
158+
159+
global.view._preview.onGetProjectAnnotationsChanged({
160+
getProjectAnnotations: getProjectAnnotations(global.view, annotations),
161+
});
162+
}
163+
164+
export const view = global.view;
90165
"
91166
`;
92167

93168
exports[`loader writeRequires when there is no preview does not add preview related stuff 1`] = `
94169
"
95170
/* do not change this file, it is auto generated by storybook. */
96-
97-
import { start } from '@storybook/react-native';
171+
172+
import { start, prepareStories, getProjectAnnotations } from '@storybook/react-native';
98173
99174
import "@storybook/addon-ondevice-notes/register";
100175
import "@storybook/addon-ondevice-controls/register";
@@ -108,14 +183,39 @@ import "@storybook/addon-ondevice-actions/register";
108183
importPathMatcher: /^\\.[\\\\/](?:FakeStory\\.stories\\.tsx)$/,
109184
// @ts-ignore
110185
req: require.context('./', false, /^\\.[\\\\/](?:FakeStory\\.stories\\.tsx)$/)
111-
}]
186+
}];
112187
113-
// @ts-ignore
114-
global.STORIES = normalizedStories;
188+
189+
declare global {
190+
var view: ReturnType<typeof start>;
191+
var STORIES: typeof normalizedStories;
192+
}
193+
194+
195+
const annotations = [require("@storybook/react-native/dist/preview"), require('@storybook/addon-actions/preview')];
115196
116-
export const view = start({
117-
annotations: [require("@storybook/react-native/dist/preview"), require('@storybook/addon-actions/preview')],
118-
storyEntries: normalizedStories
119-
});
197+
global.STORIES = normalizedStories;
198+
199+
// @ts-ignore
200+
module?.hot?.accept?.();
201+
202+
if (!global.view) {
203+
global.view = start({
204+
annotations,
205+
storyEntries: normalizedStories
206+
});
207+
} else {
208+
const { importMap } = prepareStories({ storyEntries: normalizedStories });
209+
210+
global.view._preview.onStoriesChanged({
211+
importFn: async (importPath: string) => importMap[importPath],
212+
});
213+
214+
global.view._preview.onGetProjectAnnotationsChanged({
215+
getProjectAnnotations: getProjectAnnotations(global.view, annotations),
216+
});
217+
}
218+
219+
export const view = global.view;
120220
"
121221
`;

packages/react-native/scripts/generate.js

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,52 @@ function generate({ configPath, absolute = false, useJs = false }) {
5959

6060
const annotations = `[${previewExists ? "require('./preview')," : ''}${doctools}, ${enhancer}]`;
6161

62+
const globalTypes = `
63+
declare global {
64+
var view: ReturnType<typeof start>;
65+
var STORIES: typeof normalizedStories;
66+
}
67+
`;
68+
6269
const fileContent = `
6370
/* do not change this file, it is auto generated by storybook. */
64-
65-
import { start } from '@storybook/react-native';
71+
72+
import { start, prepareStories, getProjectAnnotations } from '@storybook/react-native';
6673
6774
${registerAddons}
6875
69-
const normalizedStories = [${normalizedStories.join(',')}]
76+
const normalizedStories = [${normalizedStories.join(',')}];
7077
71-
// @ts-ignore
72-
global.STORIES = normalizedStories;
78+
${useJs ? '' : globalTypes}
7379
74-
export const view = start({
75-
annotations: ${annotations},
76-
storyEntries: normalizedStories
77-
});
80+
const annotations = ${annotations};
81+
82+
global.STORIES = normalizedStories;
83+
84+
// @ts-ignore
85+
module?.hot?.accept?.();
86+
87+
if (!global.view) {
88+
global.view = start({
89+
annotations,
90+
storyEntries: normalizedStories
91+
});
92+
} else {
93+
const { importMap } = prepareStories({ storyEntries: normalizedStories });
94+
95+
global.view._preview.onStoriesChanged({
96+
importFn: async (importPath: string) => importMap[importPath],
97+
});
98+
99+
global.view._preview.onGetProjectAnnotationsChanged({
100+
getProjectAnnotations: getProjectAnnotations(global.view, annotations),
101+
});
102+
}
103+
104+
export const view = global.view;
78105
`;
79106

80-
const formattedFileContent = prettier.format(fileContent, { parser: 'babel' });
107+
const formattedFileContent = prettier.format(fileContent, { parser: 'babel-ts' });
81108

82109
fs.writeFileSync(storybookRequiresLocation, formattedFileContent, {
83110
encoding: 'utf8',
@@ -88,16 +115,3 @@ function generate({ configPath, absolute = false, useJs = false }) {
88115
module.exports = {
89116
generate,
90117
};
91-
92-
// TODO evaluate if this is needed
93-
// if (import.meta.webpackHot) {
94-
// import.meta.webpackHot.accept('./{{storiesFilename}}', () => {
95-
// // importFn has changed so we need to patch the new one in
96-
// preview.onStoriesChanged({ importFn });
97-
// });
98-
99-
// import.meta.webpackHot.accept([{{#each previewAnnotations}}'{{this}}',{{/each}}], () => {
100-
// // getProjectAnnotations has changed so we need to patch the new one in
101-
// preview.onGetProjectAnnotationsChanged({ getProjectAnnotations });
102-
// });
103-
// }

0 commit comments

Comments
 (0)