From 42240d29d691a072faf5589d217265e82e904710 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Tue, 14 Jun 2022 21:50:02 -0400 Subject: [PATCH] Use an alias for react-dom/client if not installed This implements a suggestion in https://github.com/vitejs/vite/issues/6007#issuecomment-1155720679 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. --- .../builder-vite/code-generator-plugin.ts | 28 ++++++++++++------- packages/builder-vite/optimizeDeps.ts | 16 ----------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/packages/builder-vite/code-generator-plugin.ts b/packages/builder-vite/code-generator-plugin.ts index d6adfad9..98e763e1 100644 --- a/packages/builder-vite/code-generator-plugin.ts +++ b/packages/builder-vite/code-generator-plugin.ts @@ -15,7 +15,6 @@ import { virtualAddonSetupFile, virtualFileId, virtualPreviewFile, virtualStorie export function codeGeneratorPlugin(options: ExtendedOptions): Plugin { const iframePath = path.resolve(__dirname, '..', 'input', 'iframe.html'); let iframeId: string; - let projRoot: string; // noinspection JSUnusedGlobalSymbols return { @@ -59,9 +58,23 @@ export function codeGeneratorPlugin(options: ExtendedOptions): Plugin { input: iframePath, }; } + + // Detect if react 18 is installed. If not, alias it to a virtual placeholder file. + try { + require.resolve('react-dom/client', { paths: [config.root || process.cwd()] }); + } catch (e) { + if (isNodeError(e) && e.code === 'MODULE_NOT_FOUND') { + config.resolve = { + ...config.resolve, + alias: { + ...config.resolve?.alias, + 'react-dom/client': path.resolve(__dirname, '..', 'input', 'react-dom-client-placeholder.js'), + }, + }; + } + } }, configResolved(config) { - projRoot = config.root; iframeId = `${config.root}/iframe.html`; }, resolveId(source) { @@ -75,14 +88,6 @@ export function codeGeneratorPlugin(options: ExtendedOptions): Plugin { return virtualPreviewFile; } else if (source === virtualAddonSetupFile) { return virtualAddonSetupFile; - // Avoid error in react < 18 projects - } else if (source === 'react-dom/client') { - try { - return require.resolve('react-dom/client', { paths: [projRoot] }); - } catch (e) { - // This is not a react 18 project, need to stub out to avoid error - return path.resolve(__dirname, '..', 'input', 'react-dom-client-placeholder.js'); - } } }, async load(id) { @@ -123,3 +128,6 @@ export function codeGeneratorPlugin(options: ExtendedOptions): Plugin { }, }; } + +// Refines an error received from 'catch' to be a NodeJS exception +const isNodeError = (error: unknown): error is NodeJS.ErrnoException => error instanceof Error; diff --git a/packages/builder-vite/optimizeDeps.ts b/packages/builder-vite/optimizeDeps.ts index f41fa65b..8fa536fa 100644 --- a/packages/builder-vite/optimizeDeps.ts +++ b/packages/builder-vite/optimizeDeps.ts @@ -109,17 +109,6 @@ export async function getOptimizeDeps( const stories = absoluteStories.map((storyPath) => normalizePath(path.relative(root, storyPath))); const resolvedConfig = await resolveConfig(config, 'serve', 'development'); - const exclude = []; - // This is necessary to support react < 18 with new versions of @storybook/react that support react 18. - // TODO: narrow this down to just framework === 'react'. But causes a vue dev start problem in this monorepo. - try { - require.resolve('react-dom/client', { paths: [config.root] }); - } catch (e) { - if (isNodeError(e) && e.code === 'MODULE_NOT_FOUND') { - exclude.push('react-dom/client'); - } - } - // This function converts ids which might include ` > ` to a real path, if it exists on disk. // See https://github.com/vitejs/vite/blob/67d164392e8e9081dc3f0338c4b4b8eea6c5f7da/packages/vite/src/node/optimizer/index.ts#L182-L199 const resolve = resolvedConfig.createResolver({ asSrc: false }); @@ -131,10 +120,5 @@ export async function getOptimizeDeps( // We need Vite to precompile these dependencies, because they contain non-ESM code that would break // if we served it directly to the browser. include, - // In some cases we need to prevent deps from being pre-bundled - exclude, }; } - -// Refines an error received from 'catch' to be a NodeJS exception -const isNodeError = (error: unknown): error is NodeJS.ErrnoException => error instanceof Error;