Skip to content

Commit 4261de7

Browse files
nickytonlinetaty2010kodiakhq[bot]
authored
fix: improves support for __NEXT_PRIVATE_PREBUNDLED_REACT (#2236)
* fix: improves support for __NEXT_PRIVATE_PREBUNDLED_REACT * chore: checks for server actions to determine experimental or next version of React * chore: auto-fixed lint/prettier issues * chore: fixed comment * chore: fixed comment * fix: tn/ moving Nicks try/catch to requirehooks * fix: spelling * fix: parsedUrl check for i18n * fix: updated match for basepath/locale * chore: added test for basePath w/ grouping --------- Co-authored-by: taty2010 <[email protected]> Co-authored-by: Tatyana <[email protected]> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent 1e910d7 commit 4261de7

File tree

5 files changed

+74
-13
lines changed

5 files changed

+74
-13
lines changed

packages/runtime/src/templates/getHandler.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,7 @@ const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mod
108108
const server = new Server(async (req, res) => {
109109
try {
110110
await requestHandler(req, res)
111-
} catch (error) {
112-
console.error(error)
111+
} catch {
113112
throw new Error('Error handling request. See function logs for details.')
114113
}
115114
})

packages/runtime/src/templates/handlerUtils.ts

+28-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import { promisify } from 'util'
77

88
import { HandlerEvent, HandlerResponse } from '@netlify/functions'
99
import { http, https } from 'follow-redirects'
10-
import type NextNodeServer from 'next/dist/server/next-server'
10+
import NextNodeServer from 'next/dist/server/next-server'
11+
12+
import type { StaticRoute } from '../helpers/types'
1113

1214
export type NextServerType = typeof NextNodeServer
1315

@@ -271,3 +273,28 @@ export const localizeDataRoute = (dataRoute: string, localizedRoute: string): st
271273
.replace(new RegExp(`/_next/data/(.+?)/(${locale}/)?`), `/_next/data/$1/${locale}/`)
272274
.replace(/\/index\.json$/, '.json')
273275
}
276+
277+
export const getMatchedRoute = (
278+
paths: string,
279+
routesManifest: Array<StaticRoute>,
280+
parsedUrl: string,
281+
basePath: string,
282+
trailingSlash: boolean,
283+
// eslint-disable-next-line max-params
284+
): StaticRoute =>
285+
routesManifest?.find((route) => {
286+
// Some internationalized routes are automatically removing the locale prefix making the path not match the route
287+
// we can use the parsedURL, which has the locale included and will match
288+
const base = '/'
289+
return new RegExp(route.regex).test(
290+
new URL(
291+
// If using basepath config, we have to use the original path to match the route
292+
// This seems to only be an issue on the index page when using group routes
293+
parsedUrl ||
294+
(basePath && paths === (trailingSlash && !basePath?.endsWith('/') ? `${basePath}/` : basePath)
295+
? base
296+
: paths),
297+
'http://n',
298+
).pathname,
299+
)
300+
})

packages/runtime/src/templates/requireHooks.ts

-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ export const applyRequireHooks = () => {
112112
) {
113113
const reactMode = process.env.__NEXT_PRIVATE_PREBUNDLED_REACT || 'default'
114114
const resolvedRequest = hooks.get(reactMode)?.get(request) ?? request
115-
116115
return originalResolveFilename.call(mod, resolvedRequest, parent, isMain, options)
117116

118117
// We use `bind` here to avoid referencing outside variables to create potential memory leaks.

packages/runtime/src/templates/server.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// eslint-disable-next-line n/no-deprecated-api -- this is what Next.js uses as well
22
import { parse } from 'url'
33

4+
import { NextConfig } from 'next'
45
import type { PrerenderManifest } from 'next/dist/build'
56
import type { BaseNextResponse } from 'next/dist/server/base-http'
67
import type { NodeRequestHandler, Options } from 'next/dist/server/next-server'
@@ -12,6 +13,7 @@ import {
1213
localizeRoute,
1314
localizeDataRoute,
1415
unlocalizeRoute,
16+
getMatchedRoute,
1517
} from './handlerUtils'
1618

1719
interface NetlifyConfig {
@@ -24,6 +26,10 @@ const getNetlifyNextServer = (NextServer: NextServerType) => {
2426
private netlifyConfig: NetlifyConfig
2527
private netlifyPrerenderManifest: PrerenderManifest
2628

29+
public getAppRouterReactVersion(): string {
30+
return this.nextConfig.experimental?.serverActions ? 'experimental' : 'next'
31+
}
32+
2733
public constructor(options: Options, netlifyConfig: NetlifyConfig) {
2834
super(options)
2935
this.netlifyConfig = netlifyConfig
@@ -47,7 +53,7 @@ const getNetlifyNextServer = (NextServer: NextServerType) => {
4753
const { url, headers } = req
4854

4955
// conditionally use the prebundled React module
50-
this.netlifyPrebundleReact(url)
56+
this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl)
5157

5258
// intercept on-demand revalidation requests and handle with the Netlify API
5359
if (headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) {
@@ -76,24 +82,20 @@ const getNetlifyNextServer = (NextServer: NextServerType) => {
7682
}
7783

7884
// doing what they do in https://github.com/vercel/vercel/blob/1663db7ca34d3dd99b57994f801fb30b72fbd2f3/packages/next/src/server-build.ts#L576-L580
79-
private netlifyPrebundleReact(path: string) {
85+
private async netlifyPrebundleReact(path: string, { basePath, trailingSlash }: NextConfig, parsedUrl) {
8086
const routesManifest = this.getRoutesManifest?.()
8187
const appPathsRoutes = this.getAppPathRoutes?.()
82-
8388
const routes = routesManifest && [...routesManifest.staticRoutes, ...routesManifest.dynamicRoutes]
84-
const matchedRoute = routes?.find((route) => new RegExp(route.regex).test(new URL(path, 'http://n').pathname))
89+
const matchedRoute = await getMatchedRoute(path, routes, parsedUrl, basePath, trailingSlash)
8590
const isAppRoute = appPathsRoutes && matchedRoute ? appPathsRoutes[matchedRoute.page] : false
8691

8792
if (isAppRoute) {
8893
// app routes should use prebundled React
8994
// eslint-disable-next-line no-underscore-dangle
90-
process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = this.nextConfig.experimental?.serverActions
91-
? 'experimental'
92-
: 'next'
95+
process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = this.getAppRouterReactVersion()
96+
9397
return
9498
}
95-
96-
// pages routes should use use node_modules React
9799
// eslint-disable-next-line no-underscore-dangle
98100
process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = ''
99101
}

test/templates/server.spec.ts

+34
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ jest.mock(
6868
jest.mock(
6969
'routes-manifest.json',
7070
() => ({
71+
basePath: '',
7172
dynamicRoutes: [
7273
{
7374
page: '/posts/[title]',
@@ -88,6 +89,12 @@ jest.mock(
8889
},
8990
],
9091
staticRoutes: [
92+
{
93+
namedRegex: '^/(?:/)?$',
94+
page: '/',
95+
regex: '^/(?:/)?$',
96+
routeKeys: {},
97+
},
9198
{
9299
page: '/non-i18n/with-revalidate',
93100
regex: '^/non-i18n/with-revalidate(?:/)?$',
@@ -101,11 +108,23 @@ jest.mock(
101108
namedRegex: '^/i18n/with-revalidate(?:/)?$',
102109
},
103110
],
111+
redirects: [
112+
{
113+
basePath: false,
114+
destination: '/docs/',
115+
internal: true,
116+
locale: false,
117+
regex: '^/docs$',
118+
source: '/docs',
119+
statusCode: 308,
120+
},
121+
],
104122
}),
105123
{ virtual: true },
106124
)
107125

108126
const appPathsManifest = {
127+
'/(group)/page': 'app/(group)/page.js',
109128
'/blog/(test)/[author]/[slug]/page': 'app/blog/[author]/[slug]/page.js',
110129
}
111130

@@ -312,4 +331,19 @@ describe('the netlify next server', () => {
312331
// eslint-disable-next-line no-underscore-dangle
313332
expect(process.env.__NEXT_PRIVATE_PREBUNDLED_REACT).toBe('experimental')
314333
})
334+
335+
it('assigns correct prebundled react with basePath config using appdir', async () => {
336+
const netlifyNextServer = new NetlifyNextServer({ conf: { experimental: { appDir: true }, basePath: '/docs' } }, {})
337+
const requestHandler = netlifyNextServer.getRequestHandler()
338+
339+
const { req: mockReq, res: mockRes } = createRequestResponseMocks({
340+
url: '/docs',
341+
})
342+
343+
// @ts-expect-error - Types are incorrect for `MockedResponse`
344+
await requestHandler(new NodeNextRequest(mockReq), new NodeNextResponse(mockRes))
345+
346+
// eslint-disable-next-line no-underscore-dangle
347+
expect(process.env.__NEXT_PRIVATE_PREBUNDLED_REACT).toBe('next')
348+
})
315349
})

0 commit comments

Comments
 (0)