Skip to content

feat: add Generator to edge functions #2019

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 38 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
76d4250
feat: updated funcmetadata to include edge
taty2010 Mar 28, 2023
bba7172
feat: add generator to edge
taty2010 Mar 28, 2023
a522350
chore: prettier
taty2010 Mar 28, 2023
9e4b2bb
Merge branch 'main' into tn/generator-edgeFunc
taty2010 Mar 28, 2023
3158b22
feat: refactored nextpluginversion to use within writeEdge
taty2010 Mar 28, 2023
ade4960
fix: updated func causing tests fails
taty2010 Mar 30, 2023
04b5b8f
Merge branch 'main' into tn/generator-edgeFunc
taty2010 Mar 30, 2023
cfebd77
feat: added testing for edge function generator
taty2010 Mar 30, 2023
7426594
Merge branch 'main' into tn/generator-edgeFunc
taty2010 Mar 31, 2023
3f0c121
chore: removed console.log
taty2010 Apr 3, 2023
9c55d4b
Update variable naming to match constants
taty2010 Apr 3, 2023
f69e2ab
fix: fixed naming
taty2010 Apr 3, 2023
7f89ed3
Update test/functionsMetaData.spec.ts
taty2010 Apr 3, 2023
1fbbe28
chore: refactoring test
taty2010 Apr 3, 2023
6650517
chore: added functionManifest interface within tests to catch errors
taty2010 Apr 3, 2023
d6e073d
chore: removed comment
taty2010 Apr 3, 2023
6c0fada
chore: removed comment
taty2010 Apr 4, 2023
530f493
Merge branch 'main' into tn/generator-edgeFunc
taty2010 Apr 4, 2023
98a7f1f
chore: updated naming, refactored functionality, and removed tests
taty2010 Apr 5, 2023
22f1962
chore: added new tests and added generator to rsc in edge
taty2010 Apr 7, 2023
7f6f806
Merge branch 'main' into tn/generator-edgeFunc
taty2010 Apr 7, 2023
fc0e015
chore: updated snapshot
taty2010 Apr 7, 2023
af9ceac
fix: snapshot update with most recent changes
taty2010 Apr 7, 2023
07f997c
fix: new tests snapshot fix
taty2010 Apr 10, 2023
b701ddb
fix: testing
taty2010 Apr 10, 2023
75b214a
fix: testing to see if tests pass after removing new test/snpshot
taty2010 Apr 10, 2023
8b2a424
chore: adding new tests back in
taty2010 Apr 10, 2023
92d7afe
Merge branch 'main' into tn/generator-edgeFunc
taty2010 Apr 10, 2023
0b5f70c
chore: removed string literals
taty2010 Apr 14, 2023
472bde3
fix: updated tests to now sort manifest data
taty2010 Apr 14, 2023
f0ffb8f
Merge branch 'main' into tn/generator-edgeFunc
taty2010 Apr 14, 2023
2dc856a
chore: renaming and adding gen to dev edge
taty2010 Apr 17, 2023
430e985
fix: snapshot
taty2010 Apr 17, 2023
3602e86
Merge branch 'main' into tn/generator-edgeFunc
taty2010 Apr 17, 2023
85f8f22
fix: removed snapshot testing and updated rsc tests
taty2010 Apr 18, 2023
0f3252e
chore: forgot to remove obsolete snapshots
taty2010 Apr 18, 2023
63e791d
chore: sort not needed
taty2010 Apr 19, 2023
bc9dae5
Merge branch 'main' into tn/generator-edgeFunc
taty2010 Apr 19, 2023
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
18 changes: 14 additions & 4 deletions packages/runtime/src/helpers/edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import { outdent } from 'outdent'
import { IMAGE_FUNCTION_NAME } from '../constants'

import { getRequiredServerFiles, NextConfig } from './config'
import { writeEdgeFunctionConfiguration } from './functionsMetaData'
import { makeLocaleOptional, stripLookahead, transformCaptureGroups } from './matchers'
import { RoutesManifest } from './types'

// This is the format as of [email protected]
interface EdgeFunctionDefinitionV1 {
env: string[]
Expand Down Expand Up @@ -57,12 +57,14 @@ export interface FunctionManifest {
name?: string
path: string
cache?: 'manual'
generator?: string
}
| {
function: string
name?: string
pattern: string
cache?: 'manual'
generator?: string
}
>
import_map?: string
Expand Down Expand Up @@ -220,15 +222,17 @@ const generateEdgeFunctionMiddlewareMatchers = ({
const middlewareMatcherToEdgeFunctionDefinition = (
matcher: MiddlewareMatcher,
name: string,
generator: string,
cache?: 'manual',
): {
function: string
name?: string
pattern: string
cache?: 'manual'
generator?: string
} => {
const pattern = transformCaptureGroups(stripLookahead(matcher.regexp))
return { function: name, pattern, name, cache }
return { function: name, pattern, name, cache, generator }
}

export const cleanupEdgeFunctions = ({
Expand Down Expand Up @@ -343,6 +347,8 @@ export const writeEdgeFunctions = async ({
netlifyConfig: NetlifyConfig
routesManifest: RoutesManifest
}) => {
const nextjsPluginVersion = await writeEdgeFunctionConfiguration()

const manifest: FunctionManifest = {
functions: [],
version: 1,
Expand Down Expand Up @@ -400,7 +406,9 @@ export const writeEdgeFunctions = async ({
})

manifest.functions.push(
...matchers.map((matcher) => middlewareMatcherToEdgeFunctionDefinition(matcher, functionName)),
...matchers.map((matcher) =>
middlewareMatcherToEdgeFunctionDefinition(matcher, functionName, nextjsPluginVersion),
),
)
}
// Functions (i.e. not middleware, but edge SSR and API routes)
Expand Down Expand Up @@ -440,6 +448,7 @@ export const writeEdgeFunctions = async ({
pattern,
// cache: "manual" is currently experimental, so we restrict it to sites that use experimental appDir
cache: usesAppDir ? 'manual' : undefined,
generator: `${nextjsPluginVersion}`,
})
// pages-dir page routes also have a data route. If there's a match, add an entry mapping that to the function too
const dataRoute = dataRoutesMap.get(edgeFunctionDefinition.page)
Expand All @@ -449,6 +458,7 @@ export const writeEdgeFunctions = async ({
name: edgeFunctionDefinition.name,
pattern: dataRoute,
cache: usesAppDir ? 'manual' : undefined,
generator: `${nextjsPluginVersion}`,
})
}
}
Expand All @@ -474,6 +484,7 @@ export const writeEdgeFunctions = async ({
function: 'ipx',
name: 'next/image handler',
path: '/_next/image*',
generator: `${nextjsPluginVersion}`,
})
} else {
console.log(
Expand All @@ -487,6 +498,5 @@ export const writeEdgeFunctions = async ({
This feature is in beta. Please share your feedback here: https://ntl.fyi/next-netlify-edge
`)
}

await writeJson(join(edgeFunctionRoot, 'manifest.json'), manifest)
}
30 changes: 20 additions & 10 deletions packages/runtime/src/helpers/functionsMetaData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ const getNextRuntimeVersion = async (packageJsonPath: string, useNodeModulesPath
return useNodeModulesPath ? packagePlugin.version : packagePlugin.dependencies[NEXT_PLUGIN]
}

const pluginPackagePath = '.netlify/plugins/package.json'

const nextPluginVersion = async () => {
const moduleRoot = resolveModuleRoot(NEXT_PLUGIN)
const nodeModulesPath = moduleRoot ? join(moduleRoot, 'package.json') : null

return (
(await getNextRuntimeVersion(nodeModulesPath, true)) ||
(await getNextRuntimeVersion(pluginPackagePath, false)) ||
// The runtime version should always be available, but if it's not, return 'unknown'
'unknown'
)
}
// The information needed to create a function configuration file
export interface FunctionInfo {
// The name of the function, e.g. `___netlify-handler`
Expand All @@ -34,23 +47,20 @@ export interface FunctionInfo {
*/
export const writeFunctionConfiguration = async (functionInfo: FunctionInfo) => {
const { functionName, functionTitle, functionsDir } = functionInfo
const pluginPackagePath = '.netlify/plugins/package.json'
const moduleRoot = resolveModuleRoot(NEXT_PLUGIN)
const nodeModulesPath = moduleRoot ? join(moduleRoot, 'package.json') : null

const nextPluginVersion =
(await getNextRuntimeVersion(nodeModulesPath, true)) ||
(await getNextRuntimeVersion(pluginPackagePath, false)) ||
// The runtime version should always be available, but if it's not, return 'unknown'
'unknown'
const pluginVersion = await nextPluginVersion()

const metadata = {
config: {
name: functionTitle,
generator: `${NEXT_PLUGIN_NAME}@${nextPluginVersion}`,
generator: `${NEXT_PLUGIN_NAME}@${pluginVersion}`,
},
version: 1,
}

await writeFile(join(functionsDir, functionName, `${functionName}.json`), JSON.stringify(metadata))
}

export const writeEdgeFunctionConfiguration = async () => {
const pluginVersion = await nextPluginVersion()
return `${NEXT_PLUGIN_NAME}@${pluginVersion}`
}
162 changes: 160 additions & 2 deletions test/functionsMetaData.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { readJSON } from 'fs-extra'
import { readJSON, writeJSON } from 'fs-extra'
import mock from 'mock-fs'
import { join } from 'pathe'
import { NEXT_PLUGIN_NAME } from '../packages/runtime/src/constants'
import { writeFunctionConfiguration } from '../packages/runtime/src/helpers/functionsMetaData'
import { writeEdgeFunctionConfiguration } from '../packages/runtime/src/helpers/functionsMetaData'

describe('writeFunctionConfiguration', () => {
afterEach(() => {
Expand All @@ -17,7 +18,7 @@ describe('writeFunctionConfiguration', () => {
name: 'test',
version: '1.0.0',
dependencies: {
'@netlify/plugin-nextjs': '29.3.4',
// '@netlify/plugin-nextjs': '29.3.4',
},
}),
'node_modules/@netlify/plugin-nextjs/package.json': JSON.stringify({
Expand Down Expand Up @@ -103,3 +104,160 @@ describe('writeFunctionConfiguration', () => {
expect(actual).toEqual(expected)
})
})

// EDGE FUNCTIONS

describe('writeEdgeFunctionConfiguration', () => {
afterEach(() => {
mock.restore()
})

it('should write the configuration for an edge function using node modules version of @netlify/plugin-nextjs', async () => {
const nextRuntimeVersion = '23.4.5'

mock({
'.netlify/plugins/package.json': JSON.stringify({
name: 'test',
version: '1.0.0',
dependencies: {
'@netlify/plugin-nextjs': '29.3.4',
},
}),
'node_modules/@netlify/plugin-nextjs/package.json': JSON.stringify({
name: '@netlify/plugin-nextjs',
version: nextRuntimeVersion,
}),
'.netlify/edge-functions/manifest.json': JSON.stringify({
"functions":[],
"version":1
}),
})

const manifest = {
functions:[],
version:1
}

const functionName = 'someFunction'
const name = 'someFunctionName'

const pluginVersion = await writeEdgeFunctionConfiguration()

manifest.functions.push({
functions: functionName,
name: name,
generator: pluginVersion
})

const expected = {
functions: [
{
functions: functionName,
name: name,
generator: `${NEXT_PLUGIN_NAME}@${nextRuntimeVersion}`,
}
],
version: 1,
}

const filePathToSaveTo = '.netlify/edge-functions/manifest.json'
await writeJSON(filePathToSaveTo, manifest)
const actual = await readJSON(filePathToSaveTo)

expect(actual).toEqual(expected)
})

it('should write the configuration for a function using version of @netlify/plugin-nextjs in package.json', async () => {
const nextRuntimeVersion = '23.4.5'

mock({
'.netlify/plugins/package.json': JSON.stringify({
name: 'test',
version: '1.0.0',
dependencies: {
'@netlify/plugin-nextjs': nextRuntimeVersion,
},
}),
'.netlify/edge-functions/manifest.json': JSON.stringify({
"functions":[],
"version":1
}),
})

const manifest = {
functions:[],
version:1
}

const functionName = 'someFunction'
const name = 'someFunctionName'

const pluginVersion = await writeEdgeFunctionConfiguration()

manifest.functions.push({
functions: functionName,
name: name,
generator: pluginVersion
})

const expected = {
functions: [
{
functions: functionName,
name: name,
generator: `${NEXT_PLUGIN_NAME}@${nextRuntimeVersion}`,
}
],
version: 1,
}

const filePathToSaveTo = '.netlify/edge-functions/manifest.json'
await writeJSON(filePathToSaveTo, manifest)
const actual = await readJSON(filePathToSaveTo)

expect(actual).toEqual(expected)
})

it('should write the configuration for an edge function with runtime version not found', async () => {
mock({
'.netlify/edge-functions/manifest.json': JSON.stringify({
"functions":[],
"version":1
}),
})

const manifest = {
functions:[],
version:1
}

const functionName = 'someFunction'
const name = 'someFunctionName'

const pluginVersion = await writeEdgeFunctionConfiguration()

manifest.functions.push({
functions: functionName,
name: name,
generator: pluginVersion
})

const expected = {
functions: [
{
functions: functionName,
name: name,
generator: '@netlify/next-runtime@unknown',
}
],
version: 1,
}

const filePathToSaveTo = '.netlify/edge-functions/manifest.json'
await writeJSON(filePathToSaveTo, manifest)
const actual = await readJSON(filePathToSaveTo)

expect(actual).toEqual(expected)
})
})