From d54f775362c0ebc3f348b106677c9c15305e77ef Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 1 Nov 2024 12:43:11 +0100 Subject: [PATCH] Merge pull request #29444 from sentience/fix-nextjs-react-paths-in-monorepo Next.js: Fix bundled react and react-dom in monorepos (cherry picked from commit 6c829c545ed0d28b0b9edb88713db0b21a4eb28c) --- code/frameworks/nextjs/src/config/webpack.ts | 8 ++-- code/frameworks/nextjs/src/utils.ts | 42 +++++++++++++++----- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/code/frameworks/nextjs/src/config/webpack.ts b/code/frameworks/nextjs/src/config/webpack.ts index a0ea2d47bded..76edac25c81c 100644 --- a/code/frameworks/nextjs/src/config/webpack.ts +++ b/code/frameworks/nextjs/src/config/webpack.ts @@ -36,16 +36,16 @@ export const configureConfig = async ({ addScopedAlias(baseConfig, 'react', 'next/dist/compiled/react'); } if (tryResolve('next/dist/compiled/react-dom/cjs/react-dom-test-utils.production.js')) { - setAlias( + addScopedAlias( baseConfig, 'react-dom/test-utils', 'next/dist/compiled/react-dom/cjs/react-dom-test-utils.production.js' ); } if (tryResolve('next/dist/compiled/react-dom')) { - setAlias(baseConfig, 'react-dom$', 'next/dist/compiled/react-dom'); - setAlias(baseConfig, 'react-dom/client', 'next/dist/compiled/react-dom/client'); - setAlias(baseConfig, 'react-dom/server', 'next/dist/compiled/react-dom/server'); + addScopedAlias(baseConfig, 'react-dom$', 'next/dist/compiled/react-dom'); + addScopedAlias(baseConfig, 'react-dom/client', 'next/dist/compiled/react-dom/client'); + addScopedAlias(baseConfig, 'react-dom/server', 'next/dist/compiled/react-dom/server'); } setupRuntimeConfig(baseConfig, nextConfig); diff --git a/code/frameworks/nextjs/src/utils.ts b/code/frameworks/nextjs/src/utils.ts index 198917513166..82f15d088f59 100644 --- a/code/frameworks/nextjs/src/utils.ts +++ b/code/frameworks/nextjs/src/utils.ts @@ -51,18 +51,36 @@ export const addScopedAlias = (baseConfig: WebpackConfig, name: string, alias?: }; /** - * @example // before main script path truncation require.resolve('styled-jsx') === - * '/some/path/node_modules/styled-jsx/index.js // after main script path truncation + * @example + * + * ``` + * // before main script path truncation + * require.resolve('styled-jsx') === '/some/path/node_modules/styled-jsx/index.js + * // after main script path truncation * scopedResolve('styled-jsx') === '/some/path/node_modules/styled-jsx' + * ``` + * + * @example + * + * ``` + * // referencing a named export of a package + * scopedResolve('next/dist/compiled/react-dom/client') === + * // returns the path to the package export without the script filename + * '/some/path/node_modules/next/dist/compiled/react-dom/client'; + * + * // referencing a specific file within a CJS package + * scopedResolve('next/dist/compiled/react-dom/cjs/react-dom-test-utils.production.js') === + * // returns the path to the physical file, including the script filename + * '/some/path/node_modules/next/dist/compiled/react-dom/cjs/react-dom-test-utils.production.js'; + * ``` * - * @param id The module id - * @returns A path to the module id scoped to the project folder without the main script path at the - * end + * @param id The module id or script file to resolve + * @returns An absolute path to the specified module id or script file scoped to the project folder * @summary * This is to help the addon in development. * Without it, the addon resolves packages in its node_modules instead of the example's node_modules. * Because require.resolve will also include the main script as part of the path, this function strips - * that to just include the path to the module folder + * that to just include the path to the module folder when the id provided is a package or named export. */ export const scopedResolve = (id: string): string => { let scopedModulePath; @@ -74,9 +92,15 @@ export const scopedResolve = (id: string): string => { scopedModulePath = require.resolve(id); } - const moduleFolderStrPosition = scopedModulePath.lastIndexOf( - id.replace(/\//g /* all '/' occurances */, sep) - ); + const idWithNativePathSep = id.replace(/\//g /* all '/' occurrences */, sep); + + // If the id referenced the file specifically, return the full module path & filename + if (scopedModulePath.endsWith(idWithNativePathSep)) { + return scopedModulePath; + } + + // Otherwise, return just the path to the module folder or named export + const moduleFolderStrPosition = scopedModulePath.lastIndexOf(idWithNativePathSep); const beginningOfMainScriptPath = moduleFolderStrPosition + id.length; return scopedModulePath.substring(0, beginningOfMainScriptPath); };