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
44import { glob } from 'fast-glob'
55import { 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(
4041const 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+
83142const writeHandlerFile = async (
84143 middleware : Required < OnBuildCompleteContext [ 'outputs' ] > [ 'middleware' ] ,
85144 nextConfig : NextConfigComplete ,
0 commit comments