Skip to content

Commit 3bf80ae

Browse files
committed
handle node middleware
1 parent bffff19 commit 3bf80ae

File tree

3 files changed

+76
-16
lines changed

3 files changed

+76
-16
lines changed

adapters-notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
handler and convert those files into blobs to upload later
5252
- [partially done - for edge runtime] use middleware output to generate middleware edge function
5353
- [done] don't glob for static files and use `outputs.staticFiles` instead
54+
- check `output: 'export'` case
5455
- note any remaining manual manifest files reading in build plugin once everything that could be
5556
adjusted was handled
5657

edge-runtime/shim/node.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { AsyncLocalStorage } from 'node:async_hooks'
66
import { createRequire } from 'node:module' // used in dynamically generated part
77
import process from 'node:process'
88

9-
import { registerCJSModules } from '../edge-runtime/lib/cjs.ts' // used in dynamically generated part
9+
import { registerCJSModules } from './edge-runtime/lib/cjs.ts' // used in dynamically generated part
1010

1111
globalThis.process = process
1212

src/adapter/middleware.ts

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { cp, mkdir, readFile, writeFile } from 'node:fs/promises'
2-
import { dirname, join, parse } from 'node:path'
1+
import { cp, mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises'
2+
import { dirname, join, parse, relative } from 'node:path/posix'
33

44
import { glob } from 'fast-glob'
55
import { pathToRegexp } from 'path-to-regexp'
@@ -26,12 +26,13 @@ export async function onBuildComplete(
2626
return frameworksAPIConfig
2727
}
2828

29-
if (middleware.runtime !== 'edge') {
30-
// TODO: nodejs middleware
31-
return frameworksAPIConfig
29+
if (middleware.runtime === 'edge') {
30+
await copyHandlerDependenciesForEdgeMiddleware(middleware)
31+
} else if (middleware.runtime === 'nodejs') {
32+
// return frameworksAPIConfig
33+
await copyHandlerDependenciesForNodeMiddleware(middleware, ctx.repoRoot)
3234
}
3335

34-
await copyHandlerDependenciesForEdgeMiddleware(middleware)
3536
await writeHandlerFile(middleware, ctx.config)
3637

3738
return frameworksAPIConfig
@@ -40,8 +41,6 @@ export async function onBuildComplete(
4041
const copyHandlerDependenciesForEdgeMiddleware = async (
4142
middleware: Required<OnBuildCompleteContext['outputs']>['middleware'],
4243
) => {
43-
// const srcDir = join(ctx.standaloneDir, ctx.nextDistDir)
44-
4544
const edgeRuntimeDir = join(PLUGIN_DIR, 'edge-runtime')
4645
const shimPath = join(edgeRuntimeDir, 'shim/edge.js')
4746
const shim = await readFile(shimPath, 'utf8')
@@ -58,15 +57,15 @@ const copyHandlerDependenciesForEdgeMiddleware = async (
5857
// }
5958
// }
6059

61-
for (const [relative, absolute] of Object.entries(middleware.assets)) {
62-
if (absolute.endsWith('.wasm')) {
63-
const data = await readFile(absolute)
60+
for (const [relativePath, absolutePath] of Object.entries(middleware.assets)) {
61+
if (absolutePath.endsWith('.wasm')) {
62+
const data = await readFile(absolutePath)
6463

65-
const { name } = parse(relative)
64+
const { name } = parse(relativePath)
6665
parts.push(`const ${name} = Uint8Array.from(${JSON.stringify([...data])})`)
67-
} else if (absolute.endsWith('.js')) {
68-
const entrypoint = await readFile(absolute, 'utf8')
69-
parts.push(`;// Concatenated file: ${relative} \n`, entrypoint)
66+
} else if (absolutePath.endsWith('.js')) {
67+
const entrypoint = await readFile(absolutePath, 'utf8')
68+
parts.push(`;// Concatenated file: ${relativePath} \n`, entrypoint)
7069
}
7170
}
7271
parts.push(
@@ -80,6 +79,66 @@ const copyHandlerDependenciesForEdgeMiddleware = async (
8079
await writeFile(outputFile, parts.join('\n'))
8180
}
8281

82+
const copyHandlerDependenciesForNodeMiddleware = async (
83+
middleware: Required<OnBuildCompleteContext['outputs']>['middleware'],
84+
repoRoot: string,
85+
) => {
86+
const edgeRuntimeDir = join(PLUGIN_DIR, 'edge-runtime')
87+
const shimPath = join(edgeRuntimeDir, 'shim/node.js')
88+
const shim = await readFile(shimPath, 'utf8')
89+
90+
const parts = [shim]
91+
92+
const files: string[] = Object.values(middleware.assets)
93+
if (!files.includes(middleware.filePath)) {
94+
files.push(middleware.filePath)
95+
}
96+
97+
// C++ addons are not supported
98+
const unsupportedDotNodeModules = files.filter((file) => file.endsWith('.node'))
99+
if (unsupportedDotNodeModules.length !== 0) {
100+
throw new Error(
101+
`Usage of unsupported C++ Addon(s) found in Node.js Middleware:\n${unsupportedDotNodeModules.map((file) => `- ${file}`).join('\n')}\n\nCheck https://docs.netlify.com/build/frameworks/framework-setup-guides/nextjs/overview/#limitations for more information.`,
102+
)
103+
}
104+
105+
parts.push(`const virtualModules = new Map();`)
106+
107+
const handleFileOrDirectory = async (fileOrDir: string) => {
108+
const stats = await stat(fileOrDir)
109+
if (stats.isDirectory()) {
110+
const filesInDir = await readdir(fileOrDir)
111+
for (const fileInDir of filesInDir) {
112+
await handleFileOrDirectory(join(fileOrDir, fileInDir))
113+
}
114+
} else {
115+
const content = await readFile(fileOrDir, 'utf8')
116+
117+
parts.push(
118+
`virtualModules.set(${JSON.stringify(relative(repoRoot, fileOrDir))}, ${JSON.stringify(content)});`,
119+
)
120+
}
121+
}
122+
123+
for (const file of files) {
124+
await handleFileOrDirectory(file)
125+
}
126+
parts.push(`registerCJSModules(import.meta.url, virtualModules);
127+
128+
const require = createRequire(import.meta.url);
129+
const handlerMod = require("./${relative(repoRoot, middleware.filePath)}");
130+
const handler = handlerMod.default || handlerMod;
131+
132+
export default handler
133+
`)
134+
135+
const outputFile = join(MIDDLEWARE_FUNCTION_DIR, `concatenated-file.js`)
136+
137+
await mkdir(dirname(outputFile), { recursive: true })
138+
139+
await writeFile(outputFile, parts.join('\n'))
140+
}
141+
83142
const writeHandlerFile = async (
84143
middleware: Required<OnBuildCompleteContext['outputs']>['middleware'],
85144
nextConfig: NextConfigComplete,

0 commit comments

Comments
 (0)