Skip to content

Commit 1a7e93d

Browse files
committed
remove no longer used build plugin middleware handling
1 parent 3bf80ae commit 1a7e93d

File tree

1 file changed

+2
-368
lines changed

1 file changed

+2
-368
lines changed

src/build/functions/edge.ts

Lines changed: 2 additions & 368 deletions
Original file line numberDiff line numberDiff line change
@@ -1,373 +1,7 @@
1-
import { cp, mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises'
2-
import { dirname, join, relative } from 'node:path/posix'
1+
import { rm } from 'node:fs/promises'
32

4-
import type { Manifest, ManifestFunction } from '@netlify/edge-functions'
5-
import { glob } from 'fast-glob'
6-
import type { FunctionsConfigManifest } from 'next-with-cache-handler-v2/dist/build/index.js'
7-
import type { EdgeFunctionDefinition as EdgeMiddlewareDefinition } from 'next-with-cache-handler-v2/dist/build/webpack/plugins/middleware-plugin.js'
8-
import { pathToRegexp } from 'path-to-regexp'
9-
10-
import { EDGE_HANDLER_NAME, PluginContext } from '../plugin-context.js'
11-
12-
type NodeMiddlewareDefinitionWithOptionalMatchers = FunctionsConfigManifest['functions'][0]
13-
type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }
14-
type NodeMiddlewareDefinition = WithRequired<
15-
NodeMiddlewareDefinitionWithOptionalMatchers,
16-
'matchers'
17-
>
18-
19-
function nodeMiddlewareDefinitionHasMatcher(
20-
definition: NodeMiddlewareDefinitionWithOptionalMatchers,
21-
): definition is NodeMiddlewareDefinition {
22-
return Array.isArray(definition.matchers)
23-
}
24-
25-
type EdgeOrNodeMiddlewareDefinition = {
26-
runtime: 'nodejs' | 'edge'
27-
// hoisting shared properties from underlying definitions for common handling
28-
name: string
29-
matchers: EdgeMiddlewareDefinition['matchers']
30-
} & (
31-
| {
32-
runtime: 'nodejs'
33-
functionDefinition: NodeMiddlewareDefinition
34-
}
35-
| {
36-
runtime: 'edge'
37-
functionDefinition: EdgeMiddlewareDefinition
38-
}
39-
)
40-
41-
const writeEdgeManifest = async (ctx: PluginContext, manifest: Manifest) => {
42-
await mkdir(ctx.edgeFunctionsDir, { recursive: true })
43-
await writeFile(join(ctx.edgeFunctionsDir, 'manifest.json'), JSON.stringify(manifest, null, 2))
44-
}
45-
46-
const copyRuntime = async (ctx: PluginContext, handlerDirectory: string): Promise<void> => {
47-
const files = await glob('edge-runtime/**/*', {
48-
cwd: ctx.pluginDir,
49-
ignore: ['**/*.test.ts'],
50-
dot: true,
51-
})
52-
await Promise.all(
53-
files.map((path) =>
54-
cp(join(ctx.pluginDir, path), join(handlerDirectory, path), { recursive: true }),
55-
),
56-
)
57-
}
58-
59-
/**
60-
* When i18n is enabled the matchers assume that paths _always_ include the
61-
* locale. We manually add an extra matcher for the original path without
62-
* the locale to ensure that the edge function can handle it.
63-
* We don't need to do this for data routes because they always have the locale.
64-
*/
65-
const augmentMatchers = (
66-
matchers: EdgeMiddlewareDefinition['matchers'],
67-
ctx: PluginContext,
68-
): EdgeMiddlewareDefinition['matchers'] => {
69-
const i18NConfig = ctx.buildConfig.i18n
70-
if (!i18NConfig) {
71-
return matchers
72-
}
73-
return matchers.flatMap((matcher) => {
74-
if (matcher.originalSource && matcher.locale !== false) {
75-
return [
76-
matcher.regexp
77-
? {
78-
...matcher,
79-
// https://github.com/vercel/next.js/blob/5e236c9909a768dc93856fdfad53d4f4adc2db99/packages/next/src/build/analysis/get-page-static-info.ts#L332-L336
80-
// Next is producing pretty broad matcher for i18n locale. Presumably rest of their infrastructure protects this broad matcher
81-
// from matching on non-locale paths. For us this becomes request entry point, so we need to narrow it down to just defined locales
82-
// otherwise users might get unexpected matches on paths like `/api*`
83-
regexp: matcher.regexp.replace(/\[\^\/\.]+/g, `(${i18NConfig.locales.join('|')})`),
84-
}
85-
: matcher,
86-
{
87-
...matcher,
88-
regexp: pathToRegexp(matcher.originalSource).source,
89-
},
90-
]
91-
}
92-
return matcher
93-
})
94-
}
95-
96-
const writeHandlerFile = async (
97-
ctx: PluginContext,
98-
{ matchers, name }: EdgeOrNodeMiddlewareDefinition,
99-
) => {
100-
const nextConfig = ctx.buildConfig
101-
const handlerName = getHandlerName({ name })
102-
const handlerDirectory = join(ctx.edgeFunctionsDir, handlerName)
103-
const handlerRuntimeDirectory = join(handlerDirectory, 'edge-runtime')
104-
105-
// Copying the runtime files. These are the compatibility layer between
106-
// Netlify Edge Functions and the Next.js edge runtime.
107-
await copyRuntime(ctx, handlerDirectory)
108-
109-
// Writing a file with the matchers that should trigger this function. We'll
110-
// read this file from the function at runtime.
111-
await writeFile(join(handlerRuntimeDirectory, 'matchers.json'), JSON.stringify(matchers))
112-
113-
// The config is needed by the edge function to match and normalize URLs. To
114-
// avoid shipping and parsing a large file at runtime, let's strip it down to
115-
// just the properties that the edge function actually needs.
116-
const minimalNextConfig = {
117-
basePath: nextConfig.basePath,
118-
i18n: nextConfig.i18n,
119-
trailingSlash: nextConfig.trailingSlash,
120-
skipMiddlewareUrlNormalize: nextConfig.skipMiddlewareUrlNormalize,
121-
}
122-
123-
await writeFile(
124-
join(handlerRuntimeDirectory, 'next.config.json'),
125-
JSON.stringify(minimalNextConfig),
126-
)
127-
128-
const htmlRewriterWasm = await readFile(
129-
join(
130-
ctx.pluginDir,
131-
'edge-runtime/vendor/deno.land/x/htmlrewriter@v1.0.0/pkg/htmlrewriter_bg.wasm',
132-
),
133-
)
134-
135-
// Writing the function entry file. It wraps the middleware code with the
136-
// compatibility layer mentioned above.
137-
await writeFile(
138-
join(handlerDirectory, `${handlerName}.js`),
139-
`
140-
import { init as htmlRewriterInit } from './edge-runtime/vendor/deno.land/x/htmlrewriter@v1.0.0/src/index.ts'
141-
import { handleMiddleware } from './edge-runtime/middleware.ts';
142-
import handler from './server/${name}.js';
143-
144-
await htmlRewriterInit({ module_or_path: Uint8Array.from(${JSON.stringify([
145-
...htmlRewriterWasm,
146-
])}) });
147-
148-
export default (req, context) => handleMiddleware(req, context, handler);
149-
`,
150-
)
151-
}
152-
153-
const copyHandlerDependenciesForEdgeMiddleware = async (
154-
ctx: PluginContext,
155-
{ name, env, files, wasm }: EdgeMiddlewareDefinition,
156-
) => {
157-
const srcDir = join(ctx.standaloneDir, ctx.nextDistDir)
158-
const destDir = join(ctx.edgeFunctionsDir, getHandlerName({ name }))
159-
160-
const edgeRuntimeDir = join(ctx.pluginDir, 'edge-runtime')
161-
const shimPath = join(edgeRuntimeDir, 'shim/edge.js')
162-
const shim = await readFile(shimPath, 'utf8')
163-
164-
const parts = [shim]
165-
166-
const outputFile = join(destDir, `server/${name}.js`)
167-
168-
if (env) {
169-
// Prepare environment variables for draft-mode (i.e. __NEXT_PREVIEW_MODE_ID, __NEXT_PREVIEW_MODE_SIGNING_KEY, __NEXT_PREVIEW_MODE_ENCRYPTION_KEY)
170-
for (const [key, value] of Object.entries(env)) {
171-
parts.push(`process.env.${key} = '${value}';`)
172-
}
173-
}
174-
175-
if (wasm?.length) {
176-
for (const wasmChunk of wasm ?? []) {
177-
const data = await readFile(join(srcDir, wasmChunk.filePath))
178-
parts.push(`const ${wasmChunk.name} = Uint8Array.from(${JSON.stringify([...data])})`)
179-
}
180-
}
181-
182-
for (const file of files) {
183-
const entrypoint = await readFile(join(srcDir, file), 'utf8')
184-
parts.push(`;// Concatenated file: ${file} \n`, entrypoint)
185-
}
186-
parts.push(
187-
`const middlewareEntryKey = Object.keys(_ENTRIES).find(entryKey => entryKey.startsWith("middleware_${name}"));`,
188-
// turbopack entries are promises so we await here to get actual entry
189-
// non-turbopack entries are already resolved, so await does not change anything
190-
`export default await _ENTRIES[middlewareEntryKey].default;`,
191-
)
192-
await mkdir(dirname(outputFile), { recursive: true })
193-
194-
await writeFile(outputFile, parts.join('\n'))
195-
}
196-
197-
const NODE_MIDDLEWARE_NAME = 'node-middleware'
198-
const copyHandlerDependenciesForNodeMiddleware = async (ctx: PluginContext) => {
199-
const name = NODE_MIDDLEWARE_NAME
200-
201-
const srcDir = join(ctx.standaloneDir, ctx.nextDistDir)
202-
const destDir = join(ctx.edgeFunctionsDir, getHandlerName({ name }))
203-
204-
const edgeRuntimeDir = join(ctx.pluginDir, 'edge-runtime')
205-
const shimPath = join(edgeRuntimeDir, 'shim/node.js')
206-
const shim = await readFile(shimPath, 'utf8')
207-
208-
const parts = [shim]
209-
210-
const entry = 'server/middleware.js'
211-
const nft = `${entry}.nft.json`
212-
const nftFilesPath = join(process.cwd(), ctx.nextDistDir, nft)
213-
const nftManifest = JSON.parse(await readFile(nftFilesPath, 'utf8'))
214-
215-
const files: string[] = nftManifest.files.map((file: string) => join('server', file))
216-
files.push(entry)
217-
218-
// files are relative to location of middleware entrypoint
219-
// we need to capture all of them
220-
// they might be going to parent directories, so first we check how many directories we need to go up
221-
const { maxParentDirectoriesPath, unsupportedDotNodeModules } = files.reduce(
222-
(acc, file) => {
223-
let dirsUp = 0
224-
let parentDirectoriesPath = ''
225-
for (const part of file.split('/')) {
226-
if (part === '..') {
227-
dirsUp += 1
228-
parentDirectoriesPath += '../'
229-
} else {
230-
break
231-
}
232-
}
233-
234-
if (file.endsWith('.node')) {
235-
// C++ addons are not supported
236-
acc.unsupportedDotNodeModules.push(join(srcDir, file))
237-
}
238-
239-
if (dirsUp > acc.maxDirsUp) {
240-
return {
241-
...acc,
242-
maxDirsUp: dirsUp,
243-
maxParentDirectoriesPath: parentDirectoriesPath,
244-
}
245-
}
246-
247-
return acc
248-
},
249-
{ maxDirsUp: 0, maxParentDirectoriesPath: '', unsupportedDotNodeModules: [] as string[] },
250-
)
251-
252-
if (unsupportedDotNodeModules.length !== 0) {
253-
throw new Error(
254-
`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.`,
255-
)
256-
}
257-
258-
const commonPrefix = relative(join(srcDir, maxParentDirectoriesPath), srcDir)
259-
260-
parts.push(`const virtualModules = new Map();`)
261-
262-
const handleFileOrDirectory = async (fileOrDir: string) => {
263-
const srcPath = join(srcDir, fileOrDir)
264-
265-
const stats = await stat(srcPath)
266-
if (stats.isDirectory()) {
267-
const filesInDir = await readdir(srcPath)
268-
for (const fileInDir of filesInDir) {
269-
await handleFileOrDirectory(join(fileOrDir, fileInDir))
270-
}
271-
} else {
272-
const content = await readFile(srcPath, 'utf8')
273-
274-
parts.push(
275-
`virtualModules.set(${JSON.stringify(join(commonPrefix, fileOrDir))}, ${JSON.stringify(content)});`,
276-
)
277-
}
278-
}
279-
280-
for (const file of files) {
281-
await handleFileOrDirectory(file)
282-
}
283-
parts.push(`registerCJSModules(import.meta.url, virtualModules);
284-
285-
const require = createRequire(import.meta.url);
286-
const handlerMod = require("./${join(commonPrefix, entry)}");
287-
const handler = handlerMod.default || handlerMod;
288-
289-
export default handler
290-
`)
291-
292-
const outputFile = join(destDir, `server/${name}.js`)
293-
294-
await mkdir(dirname(outputFile), { recursive: true })
295-
296-
await writeFile(outputFile, parts.join('\n'))
297-
}
298-
299-
const createEdgeHandler = async (
300-
ctx: PluginContext,
301-
definition: EdgeOrNodeMiddlewareDefinition,
302-
): Promise<void> => {
303-
await (definition.runtime === 'edge'
304-
? copyHandlerDependenciesForEdgeMiddleware(ctx, definition.functionDefinition)
305-
: copyHandlerDependenciesForNodeMiddleware(ctx))
306-
await writeHandlerFile(ctx, definition)
307-
}
308-
309-
const getHandlerName = ({ name }: Pick<EdgeMiddlewareDefinition, 'name'>): string =>
310-
`${EDGE_HANDLER_NAME}-${name.replace(/\W/g, '-')}`
311-
312-
const buildHandlerDefinition = (
313-
ctx: PluginContext,
314-
def: EdgeOrNodeMiddlewareDefinition,
315-
): Array<ManifestFunction> => {
316-
const functionHandlerName = getHandlerName({ name: def.name })
317-
const functionName = 'Next.js Middleware Handler'
318-
const cache = def.name.endsWith('middleware') ? undefined : ('manual' as const)
319-
const generator = `${ctx.pluginName}@${ctx.pluginVersion}`
320-
321-
return augmentMatchers(def.matchers, ctx).map((matcher) => ({
322-
function: functionHandlerName,
323-
name: functionName,
324-
pattern: matcher.regexp,
325-
cache,
326-
generator,
327-
}))
328-
}
3+
import { PluginContext } from '../plugin-context.js'
3294

3305
export const clearStaleEdgeHandlers = async (ctx: PluginContext) => {
3316
await rm(ctx.edgeFunctionsDir, { recursive: true, force: true })
3327
}
333-
334-
export const createEdgeHandlers = async (ctx: PluginContext) => {
335-
// Edge middleware
336-
const nextManifest = await ctx.getMiddlewareManifest()
337-
const middlewareDefinitions: EdgeOrNodeMiddlewareDefinition[] = [
338-
...Object.values(nextManifest.middleware),
339-
].map((edgeDefinition) => {
340-
return {
341-
runtime: 'edge',
342-
functionDefinition: edgeDefinition,
343-
name: edgeDefinition.name,
344-
matchers: edgeDefinition.matchers,
345-
}
346-
})
347-
348-
// Node middleware
349-
const functionsConfigManifest = await ctx.getFunctionsConfigManifest()
350-
if (
351-
functionsConfigManifest?.functions?.['/_middleware'] &&
352-
nodeMiddlewareDefinitionHasMatcher(functionsConfigManifest?.functions?.['/_middleware'])
353-
) {
354-
middlewareDefinitions.push({
355-
runtime: 'nodejs',
356-
functionDefinition: functionsConfigManifest?.functions?.['/_middleware'],
357-
name: NODE_MIDDLEWARE_NAME,
358-
matchers: functionsConfigManifest?.functions?.['/_middleware']?.matchers,
359-
})
360-
}
361-
362-
await Promise.all(middlewareDefinitions.map((def) => createEdgeHandler(ctx, def)))
363-
364-
const netlifyDefinitions = middlewareDefinitions.flatMap((def) =>
365-
buildHandlerDefinition(ctx, def),
366-
)
367-
368-
const netlifyManifest: Manifest = {
369-
version: 1,
370-
functions: netlifyDefinitions,
371-
}
372-
await writeEdgeManifest(ctx, netlifyManifest)
373-
}

0 commit comments

Comments
 (0)