Skip to content

Commit 42240d2

Browse files
committed
Use an alias for react-dom/client if not installed
This implements a suggestion in vitejs/vite#6007 (comment) to alias react-dom/client to our placeholder file when the dependency is not found. In vite 3.0, our current approach will no longer work, and we'll need to use this method instead.
1 parent 305b51f commit 42240d2

File tree

2 files changed

+18
-26
lines changed

2 files changed

+18
-26
lines changed

packages/builder-vite/code-generator-plugin.ts

+18-10
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { virtualAddonSetupFile, virtualFileId, virtualPreviewFile, virtualStorie
1515
export function codeGeneratorPlugin(options: ExtendedOptions): Plugin {
1616
const iframePath = path.resolve(__dirname, '..', 'input', 'iframe.html');
1717
let iframeId: string;
18-
let projRoot: string;
1918

2019
// noinspection JSUnusedGlobalSymbols
2120
return {
@@ -59,9 +58,23 @@ export function codeGeneratorPlugin(options: ExtendedOptions): Plugin {
5958
input: iframePath,
6059
};
6160
}
61+
62+
// Detect if react 18 is installed. If not, alias it to a virtual placeholder file.
63+
try {
64+
require.resolve('react-dom/client', { paths: [config.root || process.cwd()] });
65+
} catch (e) {
66+
if (isNodeError(e) && e.code === 'MODULE_NOT_FOUND') {
67+
config.resolve = {
68+
...config.resolve,
69+
alias: {
70+
...config.resolve?.alias,
71+
'react-dom/client': path.resolve(__dirname, '..', 'input', 'react-dom-client-placeholder.js'),
72+
},
73+
};
74+
}
75+
}
6276
},
6377
configResolved(config) {
64-
projRoot = config.root;
6578
iframeId = `${config.root}/iframe.html`;
6679
},
6780
resolveId(source) {
@@ -75,14 +88,6 @@ export function codeGeneratorPlugin(options: ExtendedOptions): Plugin {
7588
return virtualPreviewFile;
7689
} else if (source === virtualAddonSetupFile) {
7790
return virtualAddonSetupFile;
78-
// Avoid error in react < 18 projects
79-
} else if (source === 'react-dom/client') {
80-
try {
81-
return require.resolve('react-dom/client', { paths: [projRoot] });
82-
} catch (e) {
83-
// This is not a react 18 project, need to stub out to avoid error
84-
return path.resolve(__dirname, '..', 'input', 'react-dom-client-placeholder.js');
85-
}
8691
}
8792
},
8893
async load(id) {
@@ -123,3 +128,6 @@ export function codeGeneratorPlugin(options: ExtendedOptions): Plugin {
123128
},
124129
};
125130
}
131+
132+
// Refines an error received from 'catch' to be a NodeJS exception
133+
const isNodeError = (error: unknown): error is NodeJS.ErrnoException => error instanceof Error;

packages/builder-vite/optimizeDeps.ts

-16
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,6 @@ export async function getOptimizeDeps(
109109
const stories = absoluteStories.map((storyPath) => normalizePath(path.relative(root, storyPath)));
110110
const resolvedConfig = await resolveConfig(config, 'serve', 'development');
111111

112-
const exclude = [];
113-
// This is necessary to support react < 18 with new versions of @storybook/react that support react 18.
114-
// TODO: narrow this down to just framework === 'react'. But causes a vue dev start problem in this monorepo.
115-
try {
116-
require.resolve('react-dom/client', { paths: [config.root] });
117-
} catch (e) {
118-
if (isNodeError(e) && e.code === 'MODULE_NOT_FOUND') {
119-
exclude.push('react-dom/client');
120-
}
121-
}
122-
123112
// This function converts ids which might include ` > ` to a real path, if it exists on disk.
124113
// See https://github.com/vitejs/vite/blob/67d164392e8e9081dc3f0338c4b4b8eea6c5f7da/packages/vite/src/node/optimizer/index.ts#L182-L199
125114
const resolve = resolvedConfig.createResolver({ asSrc: false });
@@ -131,10 +120,5 @@ export async function getOptimizeDeps(
131120
// We need Vite to precompile these dependencies, because they contain non-ESM code that would break
132121
// if we served it directly to the browser.
133122
include,
134-
// In some cases we need to prevent deps from being pre-bundled
135-
exclude,
136123
};
137124
}
138-
139-
// Refines an error received from 'catch' to be a NodeJS exception
140-
const isNodeError = (error: unknown): error is NodeJS.ErrnoException => error instanceof Error;

0 commit comments

Comments
 (0)