Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure src/middleware handles correctly #75702

Merged
merged 2 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/next/src/build/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ export async function createEntrypoints(
isDev: false,
})
} else if (isMiddlewareFile(page)) {
server[serverBundlePath] = getEdgeServerEntry({
server[serverBundlePath.replace('src/', '')] = getEdgeServerEntry({
...params,
rootDir,
absolutePagePath: absolutePagePath,
Expand Down
9 changes: 8 additions & 1 deletion packages/next/src/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
SERVER_REFERENCE_MANIFEST,
APP_PATH_ROUTES_MANIFEST,
ROUTES_MANIFEST,
FUNCTIONS_CONFIG_MANIFEST,
} from '../shared/lib/constants'
import loadConfig from '../server/config'
import type { ExportPathMap } from '../server/config-shared'
Expand Down Expand Up @@ -476,7 +477,13 @@ async function exportAppImpl(
join(distDir, SERVER_DIRECTORY, MIDDLEWARE_MANIFEST)
) as MiddlewareManifest

hasMiddleware = Object.keys(middlewareManifest.middleware).length > 0
const functionsConfigManifest = require(
join(distDir, SERVER_DIRECTORY, FUNCTIONS_CONFIG_MANIFEST)
)

hasMiddleware =
Object.keys(middlewareManifest.middleware).length > 0 ||
Boolean(functionsConfigManifest.functions?.['/_middleware'])
} catch {}

// Warn if the user defines a path for an API page
Expand Down
2 changes: 1 addition & 1 deletion packages/next/src/server/dev/on-demand-entry-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ export async function findPagePathData(
let bundlePath = normalizedPagePath
let pageKey = posix.normalize(pageUrl)

if (isInstrumentation) {
if (isInstrumentation || isMiddlewareFile(normalizedPagePath)) {
bundlePath = bundlePath.replace('/src', '')
pageKey = page.replace('/src', '')
}
Expand Down
5 changes: 5 additions & 0 deletions test/integration/middleware-src-node/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
experimental: {
nodeMiddleware: true,
},
}
11 changes: 11 additions & 0 deletions test/integration/middleware-src-node/src/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { NextResponse } from 'next/server'

export const config = {
runtime: 'nodejs',
}

export default function () {
const response = NextResponse.next()
response.headers.set('X-From-Src-Middleware', 'true')
return response
}
11 changes: 11 additions & 0 deletions test/integration/middleware-src-node/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { NextResponse } from 'next/server'

export const config = {
runtime: 'nodejs',
}

export default function () {
const response = NextResponse.next()
response.headers.set('X-From-Src-Middleware-TS', 'true')
return response
}
2 changes: 2 additions & 0 deletions test/integration/middleware-src-node/src/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const Page = () => <h1>Hi from SRC</h1>
export default Page
130 changes: 130 additions & 0 deletions test/integration/middleware-src-node/test/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/* eslint-env jest */

import fs from 'fs-extra'
import { join } from 'path'
import {
fetchViaHTTP,
File,
findPort,
launchApp,
killApp,
nextBuild,
} from 'next-test-utils'

let app
let appPort
const appDir = join(__dirname, '../')
const nextConfig = new File(join(appDir, 'next.config.js'))
const srcHeader = 'X-From-Src-Middleware'
const rootHeader = 'X-From-Root-Middleware'
const rootMiddlewareJSFile = join(appDir, 'middleware.js')
const rootMiddlewareTSFile = join(appDir, 'middleware.ts')

function runSingleMiddlewareTests() {
it('loads an runs src middleware', async () => {
const response = await fetchViaHTTP(appPort, '/post-1')
expect(response.headers.has(srcHeader)).toBe(false)
expect(response.headers.has(`${srcHeader}-TS`)).toBe(true)
})
}

function runDoubleMiddlewareTests() {
it('loads and runs only root middleware', async () => {
const response = await fetchViaHTTP(appPort, '/post-1')
expect(response.headers.has(srcHeader)).toBe(false)
expect(response.headers.has(`${srcHeader}-TS`)).toBe(false)
expect(response.headers.has(rootHeader)).toBe(false)
expect(response.headers.has(`${rootHeader}-TS`)).toBe(true)
})
}

async function writeRootMiddleware() {
await fs.copy(join(appDir, 'src/pages'), join(appDir, 'pages'), {
force: true,
recursive: true,
})
await fs.writeFile(
rootMiddlewareJSFile,
`
import { NextResponse } from 'next/server'

export default function () {
const response = NextResponse.next()
response.headers.set('${rootHeader}', 'true')
return response
}`
)
await fs.writeFile(
rootMiddlewareTSFile,
`
import { NextResponse } from 'next/server'

export default function () {
const response = NextResponse.next()
response.headers.set('${rootHeader}-TS', 'true')
return response
}`
)
}

async function removeRootMiddleware() {
await fs.remove(rootMiddlewareJSFile, { force: true })
await fs.remove(rootMiddlewareTSFile, { force: true })
await fs.remove(join(appDir, 'pages'), { force: true, recursive: true })
}

describe.each([
{
title: 'Middleware in src/ folder',
setup() {},
teardown() {},
runTest: runSingleMiddlewareTests,
},
{
title: 'Middleware in src/ and / folders',
setup: writeRootMiddleware,
teardown: removeRootMiddleware,
runTest: runDoubleMiddlewareTests,
},
])('$title', ({ setup, teardown, runTest }) => {
beforeAll(() => setup())
afterAll(() => teardown())
;(process.env.TURBOPACK_BUILD ? describe.skip : describe)(
'development mode',
() => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
})
afterAll(() => killApp(app))

runTest()
}
)
;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
'production mode',
() => {
let exportOutput = ''

beforeAll(async () => {
nextConfig.write(`module.exports = { output: 'export' }`)
const result = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
})

const outdir = join(__dirname, '..', 'out')
await fs.remove(outdir).catch(() => {})

exportOutput = result.stderr + result.stdout
})
afterAll(() => nextConfig.restore())

it('should warn about middleware on export', async () => {
expect(exportOutput).toContain(
'Statically exporting a Next.js application via `next export` disables API routes and middleware.'
)
})
}
)
})
Loading