diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts index 23272df2ec9f8ec..e6f5fc1d11d8ebc 100644 --- a/packages/next/build/entries.ts +++ b/packages/next/build/entries.ts @@ -207,7 +207,7 @@ export function getEdgeServerEntry(opts: { // The Edge bundle includes the server in its entrypoint, so it has to // be in the SSR layer — we later convert the page request to the RSC layer // via a webpack rule. - layer: WEBPACK_LAYERS.client, + layer: opts.appDirLoader ? WEBPACK_LAYERS.client : undefined, } } diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index ce9c91b3db12a3c..63223dd363fc6ff 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -887,6 +887,21 @@ export default async function getBaseWebpackConfig( 'next/dist/client': 'next/dist/esm/client', 'next/dist/shared': 'next/dist/esm/shared', 'next/dist/pages': 'next/dist/esm/pages', + 'next/dist/lib': 'next/dist/esm/lib', + + // Alias the usage of next public APIs + [require.resolve('next/dist/client/link')]: + 'next/dist/esm/client/link', + [require.resolve('next/dist/client/image')]: + 'next/dist/esm/client/image', + [require.resolve('next/dist/client/script')]: + 'next/dist/esm/client/script', + [require.resolve('next/dist/client/router')]: + 'next/dist/esm/client/router', + [require.resolve('next/dist/shared/lib/head')]: + 'next/dist/esm/shared/lib/head', + [require.resolve('next/dist/shared/lib/dynamic')]: + 'next/dist/esm/shared/lib/dynamic', } : undefined), @@ -1128,7 +1143,7 @@ export default async function getBaseWebpackConfig( // Treat next internals as non-external for server layer layer === WEBPACK_LAYERS.server ? false - : /next[/\\]dist[/\\](shared|server)[/\\](?!lib[/\\](router[/\\]router|dynamic))/.test( + : /next[/\\]dist[/\\](esm[\\/])?(shared|server)[/\\](?!lib[/\\](router[/\\]router|dynamic))/.test( localRes ) @@ -1194,7 +1209,9 @@ export default async function getBaseWebpackConfig( const externalType = isEsm ? 'module' : 'commonjs' if ( - /next[/\\]dist[/\\]shared[/\\](?!lib[/\\]router[/\\]router)/.test(res) || + /next[/\\]dist[/\\](esm[\\/])?shared[/\\](?!lib[/\\]router[/\\]router)/.test( + res + ) || /next[/\\]dist[/\\]compiled[/\\].*\.[mc]?js$/.test(res) ) { return `${externalType} ${request}` diff --git a/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts b/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts index 010a94f6fa5af92..59ebcf17f55d113 100644 --- a/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts +++ b/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts @@ -97,28 +97,29 @@ export default async function edgeSSRLoader(this: any) { ${ isAppDir ? ` + import { renderToHTMLOrFlight as appRenderToHTML } from 'next/dist/esm/server/app-render' + import * as pageMod from ${JSON.stringify(pageModPath)} const Document = null - const appRenderToHTML = require('next/dist/esm/server/app-render').renderToHTMLOrFlight const pagesRenderToHTML = null - const pageMod = require(${JSON.stringify(pageModPath)}) const appMod = null const errorMod = null const error500Mod = null ` : ` - const Document = require(${stringifiedDocumentPath}).default - const appRenderToHTML = null - const pagesRenderToHTML = require('next/dist/esm/server/render').renderToHTML - const pageMod = require(${stringifiedPagePath}) - const appMod = require(${stringifiedAppPath}) - const errorMod = require(${stringifiedErrorPath}) - const error500Mod = ${ - stringified500Path ? `require(${stringified500Path})` : 'null' + import Document from ${stringifiedDocumentPath} + import { renderToHTML as pagesRenderToHTML } from 'next/dist/esm/server/render' + import * as pageMod from ${stringifiedPagePath} + import * as appMod from ${stringifiedAppPath} + import * as errorMod from ${stringifiedErrorPath} + ${ + stringified500Path + ? `import * as error500Mod from ${stringified500Path}` + : `const error500Mod = null` } + const appRenderToHTML = null ` } - const buildManifest = self.__BUILD_MANIFEST const reactLoadableManifest = self.__REACT_LOADABLE_MANIFEST const rscManifest = self.__RSC_MANIFEST diff --git a/packages/next/client/dev/error-overlay/hot-dev-client.js b/packages/next/client/dev/error-overlay/hot-dev-client.js index 8d128e38b21984d..697c99945c21adc 100644 --- a/packages/next/client/dev/error-overlay/hot-dev-client.js +++ b/packages/next/client/dev/error-overlay/hot-dev-client.js @@ -119,7 +119,7 @@ function handleWarnings(warnings) { }) if (typeof console !== 'undefined' && typeof console.warn === 'function') { - for (let i = 0; i < formatted.warnings.length; i++) { + for (let i = 0; i < formatted.warnings?.length; i++) { if (i === 5) { console.warn( 'There were more warnings in other files.\n' + diff --git a/packages/next/client/image.tsx b/packages/next/client/image.tsx index 0625fe556f542a9..80c0df3bf63a5a8 100644 --- a/packages/next/client/image.tsx +++ b/packages/next/client/image.tsx @@ -10,12 +10,12 @@ import React, { } from 'react' import Head from '../shared/lib/head' import { getImageBlurSvg } from '../shared/lib/image-blur-svg' -import { +import type { ImageConfigComplete, - imageConfigDefault, ImageLoaderProps, ImageLoaderPropsWithConfig, } from '../shared/lib/image-config' +import { imageConfigDefault } from '../shared/lib/image-config' import { ImageConfigContext } from '../shared/lib/image-config-context' import { warnOnce } from '../shared/lib/utils/warn-once' // @ts-ignore - This is replaced by webpack alias @@ -36,7 +36,7 @@ const VALID_LOADING_VALUES = ['lazy', 'eager', undefined] as const type LoadingValue = typeof VALID_LOADING_VALUES[number] type ImageConfig = ImageConfigComplete & { allSizes: number[] } -export { ImageLoaderProps } +export type { ImageLoaderProps } export type ImageLoader = (p: ImageLoaderProps) => string // Do not export - this is an internal type only diff --git a/packages/next/script.js b/packages/next/script.js index 6d257fa56f1572d..4e0f885ac60a388 100644 --- a/packages/next/script.js +++ b/packages/next/script.js @@ -1,4 +1 @@ -module.exports = - process.env.NEXT_RUNTIME === 'edge' - ? require('./dist/esm/client/script') - : require('./dist/client/script') +module.exports = require('./dist/client/script') diff --git a/test/e2e/prerender.test.ts b/test/e2e/prerender.test.ts index f5e511c444c606f..3b36acddc698729 100644 --- a/test/e2e/prerender.test.ts +++ b/test/e2e/prerender.test.ts @@ -2072,6 +2072,9 @@ describe('Prerender', () => { const { version, files } = JSON.parse(contents) expect(version).toBe(1) + console.log( + check.tests.map((item) => files.some((file) => item.test(file))) + ) expect( check.tests.every((item) => files.some((file) => item.test(file))) ).toBe(true) diff --git a/test/e2e/streaming-ssr/index.test.ts b/test/e2e/streaming-ssr/index.test.ts index 58466bdd5ba07ca..958bec7e0a5c9e8 100644 --- a/test/e2e/streaming-ssr/index.test.ts +++ b/test/e2e/streaming-ssr/index.test.ts @@ -65,6 +65,11 @@ describe('react 18 streaming SSR with custom next configs', () => { expect(html).toContain('home') }) + it('should render next/router correctly in edge runtime', async () => { + const html = await renderViaHTTP(next.url, '/router') + expect(html).toContain('link') + }) + it('should render multi-byte characters correctly in streaming', async () => { const html = await renderViaHTTP(next.url, '/multi-byte') expect(html).toContain('マルチバイト'.repeat(28)) diff --git a/test/e2e/streaming-ssr/streaming-ssr/pages/router.js b/test/e2e/streaming-ssr/streaming-ssr/pages/router.js new file mode 100644 index 000000000000000..3407d79f07b2631 --- /dev/null +++ b/test/e2e/streaming-ssr/streaming-ssr/pages/router.js @@ -0,0 +1,11 @@ +import { useRouter } from 'next/router' +import Link from 'next/link' + +export default () => { + useRouter() + return link +} + +export const config = { + runtime: 'experimental-edge', +}