Skip to content

Commit 70e69ce

Browse files
committed
Handle for Webpack
1 parent 43f69fb commit 70e69ce

File tree

9 files changed

+93
-40
lines changed

9 files changed

+93
-40
lines changed

packages/next/src/build/analysis/get-page-static-info.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
} from '../segment-config/middleware/middleware-config'
4141
import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths'
4242
import { normalizePagePath } from '../../shared/lib/page-path/normalize-page-path'
43+
import { isProxyFile } from '../utils'
4344

4445
const PARSE_PATTERN =
4546
/(?<!(_jsx|jsx-))runtime|preferredRegion|getStaticProps|getServerSideProps|generateStaticParams|export const|generateImageMetadata|generateSitemaps|middleware|proxy/
@@ -756,14 +757,17 @@ export async function getPagesPageStaticInfo({
756757
warnAboutExperimentalEdge(isAnAPIRoute ? page! : null)
757758
}
758759

759-
if (
760-
(page === `/${PROXY_FILENAME}` || page === `/src/${PROXY_FILENAME}`) &&
761-
isEdgeRuntime(resolvedRuntime)
762-
) {
763-
resolvedRuntime = SERVER_RUNTIME.nodejs
764-
const message = `Proxy does not support Edge runtime.`
760+
// Turbopack will handle during compilation with better code frame.
761+
if (!process.env.TURBOPACK && isProxyFile(page) && resolvedRuntime) {
762+
const relativePath = relative(process.cwd(), pageFilePath)
763+
const resolvedPath = relativePath.startsWith('.')
764+
? relativePath
765+
: `./${relativePath}`
766+
const message = `Route segment config is not allowed in Proxy file at "${resolvedPath}". Proxy always runs on Node.js runtime. Learn more: https://nextjs.org/docs/messages/middleware-to-proxy`
767+
765768
if (isDev) {
766-
Log.error(message)
769+
Log.errorOnce(message)
770+
resolvedRuntime = SERVER_RUNTIME.nodejs
767771
} else {
768772
throw new Error(message)
769773
}
@@ -772,7 +776,7 @@ export async function getPagesPageStaticInfo({
772776
if (resolvedRuntime === SERVER_RUNTIME.edge && page && !isAnAPIRoute) {
773777
const message = `Page ${page} provided runtime 'edge', the edge runtime for rendering is currently experimental. Use runtime 'experimental-edge' instead.`
774778
if (isDev) {
775-
Log.error(message)
779+
Log.errorOnce(message)
776780
} else {
777781
throw new Error(message)
778782
}

packages/next/src/build/entries.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ import type { __ApiPreviewProps } from '../server/api-utils'
4343
import {
4444
isMiddlewareFile,
4545
isMiddlewareFilename,
46+
isProxyFile,
4647
isInstrumentationHookFile,
4748
isInstrumentationHookFilename,
48-
isProxyFilename,
4949
} from './utils'
5050
import { getPageStaticInfo } from './analysis/get-page-static-info'
5151
import { normalizePathSep } from '../shared/lib/page-path/normalize-path-sep'
@@ -767,12 +767,12 @@ export function runDependingOnPageType<T>(params: {
767767
return
768768
}
769769

770-
if (isProxyFilename(params.page)) {
770+
if (isProxyFile(params.page)) {
771771
params.onServer()
772772
return
773773
}
774774

775-
if (isMiddlewareFile(params.page)) {
775+
if (isMiddlewareFile(params.page) && !isProxyFile(params.page)) {
776776
if (params.pageRuntime === 'nodejs') {
777777
params.onServer()
778778
return

packages/next/src/build/index.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ import {
139139
isAppBuiltinPage,
140140
collectRoutesUsingEdgeRuntime,
141141
collectMeta,
142+
isProxyFile,
142143
} from './utils'
143144
import type { PageInfo, PageInfos } from './utils'
144145
import type { FallbackRouteParam, PrerenderedRoute } from './static-paths/types'
@@ -1214,13 +1215,7 @@ export default async function build(
12141215

12151216
for (const rootPath of rootPaths) {
12161217
const { name: fileBaseName, dir: fileDir } = path.parse(rootPath)
1217-
const isAtConventionLevel =
1218-
fileDir === '/' ||
1219-
fileDir === '/src' ||
1220-
// rootPaths are currently relative paths from the root directory.
1221-
// Add safety check here for unexpected future changes.
1222-
fileDir === dir ||
1223-
fileDir === path.join(dir, 'src')
1218+
const isAtConventionLevel = fileDir === '/' || fileDir === '/src'
12241219

12251220
if (isAtConventionLevel && fileBaseName === MIDDLEWARE_FILENAME) {
12261221
middlewareFilePath = rootPath
@@ -2610,30 +2605,33 @@ export default async function build(
26102605
return serverFilesManifest
26112606
})
26122607

2613-
const middlewareFile = rootPaths.find(
2614-
(p) => p.includes(MIDDLEWARE_FILENAME) || p.includes(PROXY_FILENAME)
2615-
)
2608+
const middlewareFile = proxyFilePath || middlewareFilePath
26162609
let hasNodeMiddleware = false
26172610

26182611
if (middlewareFile) {
2612+
// Is format of `(/src)/(proxy|middleware).<ext>`, so split by
2613+
// "." and get the first part, regard rest of the extensions
2614+
// to match the `page` value format.
2615+
const page = middlewareFile.split('.')[0]
2616+
26192617
const staticInfo = await getStaticInfoIncludingLayouts({
26202618
isInsideAppDir: false,
26212619
pageFilePath: path.join(dir, middlewareFile),
26222620
config,
26232621
appDir,
26242622
pageExtensions: config.pageExtensions,
26252623
isDev: false,
2626-
page: 'middleware',
2624+
page,
26272625
})
26282626

26292627
if (staticInfo.hadUnsupportedValue) {
26302628
errorFromUnsupportedSegmentConfig()
26312629
}
26322630

2633-
if (staticInfo.runtime === 'nodejs') {
2631+
if (staticInfo.runtime === 'nodejs' || isProxyFile(page)) {
26342632
hasNodeMiddleware = true
26352633
functionsConfigManifest.functions['/_middleware'] = {
2636-
runtime: staticInfo.runtime,
2634+
runtime: 'nodejs',
26372635
matchers: staticInfo.middleware?.matchers ?? [
26382636
{
26392637
regexp: '^.*$',

packages/next/src/build/output/log.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,12 @@ export function warnOnce(...message: any[]) {
8585
warn(...message)
8686
}
8787
}
88+
89+
const errorOnceCache = new LRUCache<string>(10_000, (value) => value.length)
90+
export function errorOnce(...message: any[]) {
91+
const key = message.join(' ')
92+
if (!errorOnceCache.has(key)) {
93+
errorOnceCache.set(key, key)
94+
error(...message)
95+
}
96+
}

packages/next/src/build/utils.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,6 @@ export function isMiddlewareFilename(file?: string | null) {
105105
)
106106
}
107107

108-
export function isMiddlewareOnlyFilename(file?: string | null) {
109-
return file === MIDDLEWARE_FILENAME || file === `src/${MIDDLEWARE_FILENAME}`
110-
}
111-
112-
export function isProxyFilename(file?: string | null) {
113-
return file === PROXY_FILENAME || file === `src/${PROXY_FILENAME}`
114-
}
115-
116108
export function isInstrumentationHookFilename(file?: string | null) {
117109
return (
118110
file === INSTRUMENTATION_HOOK_FILENAME ||
@@ -1429,6 +1421,10 @@ export function isMiddlewareFile(file: string) {
14291421
)
14301422
}
14311423

1424+
export function isProxyFile(file: string) {
1425+
return file === `/${PROXY_FILENAME}` || file === `/src/${PROXY_FILENAME}`
1426+
}
1427+
14321428
export function isInstrumentationHookFile(file: string) {
14331429
return (
14341430
file === `/${INSTRUMENTATION_HOOK_FILENAME}` ||

packages/next/src/server/next-server.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,13 +1574,34 @@ export default class NextNodeServer extends BaseServer<
15741574
functionsConfig?.functions?.['/_middleware']
15751575
) {
15761576
// if used with top level await, this will be a promise
1577-
return require(
1578-
join(
1579-
/* turbopackIgnore: true */ this.distDir,
1580-
'server',
1581-
'middleware.js'
1577+
// Try loading middleware.js first, then proxy.js. Instead
1578+
// of mapping proxy to middleware as the entry, just fallback
1579+
// to proxy.
1580+
// TODO: Remove this once we handle as the single entrypoint.
1581+
try {
1582+
return require(
1583+
join(
1584+
/* turbopackIgnore: true */ this.distDir,
1585+
'server',
1586+
'middleware.js'
1587+
)
15821588
)
1583-
)
1589+
} catch (middlewareErr) {
1590+
if (
1591+
isError(middlewareErr) &&
1592+
(middlewareErr.code === 'ENOENT' ||
1593+
middlewareErr.code === 'MODULE_NOT_FOUND')
1594+
) {
1595+
return require(
1596+
join(
1597+
/* turbopackIgnore: true */ this.distDir,
1598+
'server',
1599+
'proxy.js'
1600+
)
1601+
)
1602+
}
1603+
throw middlewareErr
1604+
}
15841605
}
15851606
} catch (err) {
15861607
if (
@@ -1736,7 +1757,7 @@ export default class NextNodeServer extends BaseServer<
17361757

17371758
try {
17381759
result = await adapterFn({
1739-
handler: middlewareModule.middleware || middlewareModule,
1760+
handler: middlewareModule.proxy || middlewareModule.middleware || middlewareModule,
17401761
request: {
17411762
...requestData,
17421763
body: hasRequestBody
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { nextTestSetup } from 'e2e-utils'
2+
3+
describe('proxy-runtime-nodejs', () => {
4+
const { next } = nextTestSetup({
5+
files: __dirname,
6+
})
7+
8+
it('should use nodejs runtime for proxy by default', async () => {
9+
const browser = await next.browser('/foo')
10+
expect(await browser.elementByCss('p').text()).toBe('hello world')
11+
})
12+
})
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { NextRequest, NextResponse } from 'next/server'
2+
// Will not work in edge runtime
3+
import { join } from 'path/posix'
4+
5+
export function proxy(request: NextRequest) {
6+
if (request.nextUrl.pathname === join('/', 'foo')) {
7+
return NextResponse.redirect(new URL('/', request.url))
8+
}
9+
10+
return NextResponse.next()
11+
}

test/e2e/app-dir/proxy-runtime/proxy-runtime.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ Next.js can't recognize the exported \`config\` field in route. Proxy does not s
3535
3636
The exported configuration object in a source file needs to have a very specific format from which some properties can be statically parsed at compiled-time.`)
3737
} else {
38-
expect(cliOutput).toContain(`Proxy does not support Edge runtime.`)
38+
expect(cliOutput).toContain(
39+
`Route segment config is not allowed in Proxy file at "./proxy.ts". Proxy always runs on Node.js runtime. Learn more: https://nextjs.org/docs/messages/middleware-to-proxy`
40+
)
3941
}
4042

4143
await next.stop()

0 commit comments

Comments
 (0)