Skip to content

Ensure node middleware is handled with standalone #75765

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

Merged
merged 3 commits into from
Feb 6, 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
22 changes: 22 additions & 0 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ async function writeStandaloneDirectory(
outputFileTracingRoot: string,
requiredServerFiles: RequiredServerFilesManifest,
middlewareManifest: MiddlewareManifest,
hasNodeMiddleware: boolean,
hasInstrumentationHook: boolean,
staticPages: Set<string>,
loadedEnvFiles: LoadedEnvFiles,
Expand All @@ -612,6 +613,7 @@ async function writeStandaloneDirectory(
outputFileTracingRoot,
requiredServerFiles.config,
middlewareManifest,
hasNodeMiddleware,
hasInstrumentationHook,
staticPages
)
Expand All @@ -638,6 +640,23 @@ async function writeStandaloneDirectory(
})
await fs.copyFile(filePath, outputPath)
}

if (hasNodeMiddleware) {
const middlewareOutput = path.join(
distDir,
STANDALONE_DIRECTORY,
requiredServerFiles.config.distDir,
SERVER_DIRECTORY,
'middleware.js'
)

await fs.mkdir(path.dirname(middlewareOutput), { recursive: true })
await fs.copyFile(
path.join(distDir, SERVER_DIRECTORY, 'middleware.js'),
middlewareOutput
)
}

await recursiveCopy(
path.join(distDir, SERVER_DIRECTORY, 'pages'),
path.join(
Expand Down Expand Up @@ -2324,6 +2343,7 @@ export default async function build(
const middlewareFile = rootPaths.find((p) =>
p.includes(MIDDLEWARE_FILENAME)
)
let hasNodeMiddleware = false

if (middlewareFile) {
const staticInfo = await getStaticInfoIncludingLayouts({
Expand All @@ -2337,6 +2357,7 @@ export default async function build(
})

if (staticInfo.runtime === 'nodejs') {
hasNodeMiddleware = true
functionsConfigManifest.functions['/_middleware'] = {
runtime: staticInfo.runtime,
matchers: staticInfo.middleware?.matchers ?? [
Expand Down Expand Up @@ -3543,6 +3564,7 @@ export default async function build(
outputFileTracingRoot,
requiredServerFilesManifest,
middlewareManifest,
hasNodeMiddleware,
hasInstrumentationHook,
staticPages,
loadedEnvFiles,
Expand Down
7 changes: 7 additions & 0 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1467,6 +1467,7 @@ export async function copyTracedFiles(
tracingRoot: string,
serverConfig: NextConfigComplete,
middlewareManifest: MiddlewareManifest,
hasNodeMiddleware: boolean,
hasInstrumentationHook: boolean,
staticPages: Set<string>
) {
Expand Down Expand Up @@ -1581,6 +1582,12 @@ export async function copyTracedFiles(
})
}

if (hasNodeMiddleware) {
const middlewareFile = path.join(distDir, 'server', 'middleware.js')
const middlewareTrace = `${middlewareFile}.nft.json`
await handleTraceFiles(middlewareTrace)
}

if (appPageKeys) {
for (const page of appPageKeys) {
if (middlewareManifest.functions.hasOwnProperty(page)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { NextResponse } from 'next/server'
import { ImageResponse } from 'next/og'
import fs from 'fs'
import path from 'path'

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

export async function middleware(req) {
console.log('middleware', req.url)
console.log(
'env',
await fs.promises.readFile(path.join(process.cwd(), '.env'))
)

if (req.nextUrl.pathname === '/a-non-existent-page/to-test-with-middleware') {
return new ImageResponse(<div>Hello world</div>, {
width: 1200,
height: 600,
})
}
return NextResponse.next()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
process.env.TEST_NODE_MIDDLEWARE = '1'

if (process.env.TURBOPACK) {
it('should skip for now', () => {})
} else {
require('./required-server-files.test')
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,14 @@ describe('required server files', () => {
files: {
pages: new FileRef(join(__dirname, 'pages')),
lib: new FileRef(join(__dirname, 'lib')),
'middleware.js': new FileRef(join(__dirname, 'middleware.js')),
'middleware.js': new FileRef(
join(
__dirname,
process.env.TEST_NODE_MIDDLEWARE
? 'middleware-node.js'
: 'middleware.js'
)
),
'cache-handler.js': new FileRef(join(__dirname, 'cache-handler.js')),
'data.txt': new FileRef(join(__dirname, 'data.txt')),
'.env': new FileRef(join(__dirname, '.env')),
Expand All @@ -49,6 +56,9 @@ describe('required server files', () => {
eslint: {
ignoreDuringBuilds: true,
},
experimental: {
nodeMiddleware: Boolean(process.env.TEST_NODE_MIDDLEWARE),
},
output: 'standalone',
async rewrites() {
return {
Expand Down Expand Up @@ -377,12 +387,17 @@ describe('required server files', () => {
;(process.env.TURBOPACK ? it.skip : it)(
'should output middleware correctly',
async () => {
// eslint-disable-next-line jest/no-standalone-expect
expect(
await fs.pathExists(
join(next.testDir, 'standalone/.next/server/edge-runtime-webpack.js')
)
).toBe(true)
if (!process.env.TEST_NODE_MIDDLEWARE) {
// eslint-disable-next-line jest/no-standalone-expect
expect(
await fs.pathExists(
join(
next.testDir,
'standalone/.next/server/edge-runtime-webpack.js'
)
)
).toBe(true)
}
// eslint-disable-next-line jest/no-standalone-expect
expect(
await fs.pathExists(
Expand Down Expand Up @@ -1367,14 +1382,16 @@ describe('required server files', () => {
expect(res.status).toBe(200)
expect(await res.text()).toContain('index page')

if (process.env.TURBOPACK) {
expect(
fs.existsSync(join(standaloneDir, '.next/server/edge/chunks'))
).toBe(true)
} else {
expect(
fs.existsSync(join(standaloneDir, '.next/server/edge-chunks'))
).toBe(true)
if (!process.env.TEST_NODE_MIDDLEWARE) {
if (process.env.TURBOPACK) {
expect(
fs.existsSync(join(standaloneDir, '.next/server/edge/chunks'))
).toBe(true)
} else {
expect(
fs.existsSync(join(standaloneDir, '.next/server/edge-chunks'))
).toBe(true)
}
}

const resImageResponse = await fetchViaHTTP(
Expand Down
Loading