Skip to content

Commit 3f3deb1

Browse files
committed
Merge remote-tracking branch 'origin/main' into nickytonline/blob-storage
2 parents 563a189 + e9bbc28 commit 3f3deb1

15 files changed

+330
-859
lines changed

package-lock.json

+255-826
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"@babel/preset-env": "^7.15.8",
5454
"@babel/preset-typescript": "^7.16.0",
5555
"@delucis/if-env": "^1.1.2",
56-
"@netlify/build": "^29.22.2",
56+
"@netlify/build": "^29.22.5",
5757
"@netlify/eslint-config-node": "^7.0.1",
5858
"@testing-library/cypress": "^9.0.0",
5959
"@types/fs-extra": "^9.0.13",

packages/runtime/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"manifest.yml"
1313
],
1414
"dependencies": {
15-
"@netlify/blobs": "^2.0.0",
15+
"@netlify/blobs": "2.0.0",
1616
"@netlify/esbuild": "0.14.39-1",
1717
"@netlify/functions": "^1.6.0",
1818
"@netlify/ipx": "^1.4.5",
@@ -39,7 +39,7 @@
3939
},
4040
"devDependencies": {
4141
"@delucis/if-env": "^1.1.2",
42-
"@netlify/build": "^29.22.2",
42+
"@netlify/build": "^29.22.5",
4343
"@types/fs-extra": "^9.0.13",
4444
"@types/jest": "^27.4.1",
4545
"@types/merge-stream": "^1.1.2",

packages/runtime/src/helpers/config.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ export const updateRequiredServerFiles = async (publish: string, modifiedConfig:
7676
await writeJSON(configFile, modifiedConfig)
7777
}
7878

79-
export const resolveModuleRoot = (moduleName) => {
79+
export const resolveModuleRoot = (moduleName, paths = [process.cwd()]) => {
8080
try {
81-
return dirname(relative(process.cwd(), require.resolve(`${moduleName}/package.json`, { paths: [process.cwd()] })))
81+
return dirname(relative(process.cwd(), require.resolve(`${moduleName}/package.json`, { paths })))
8282
} catch {
8383
return null
8484
}
@@ -165,6 +165,15 @@ export const configureHandlerFunctions = async ({
165165
`!${nextRoot}/dist/compiled/webpack/bundle4.js`,
166166
`!${nextRoot}/dist/compiled/webpack/bundle5.js`,
167167
)
168+
169+
// on Next 13.5+ there is no longer statically analyzable import to styled-jsx/style
170+
// so lambda fails to bundle it. Next require hooks actually try to resolve it
171+
// and fail if it is not bundled, so we forcefully add it to lambda.
172+
const styledJsxRoot = resolveModuleRoot('styled-jsx', [join(process.cwd(), nextRoot)])
173+
if (styledJsxRoot) {
174+
const styledJsxStyleModulePath = join(styledJsxRoot, 'style.js')
175+
netlifyConfig.functions[functionName].included_files.push(styledJsxStyleModulePath)
176+
}
168177
}
169178

170179
excludedModules.forEach((moduleName) => {

packages/runtime/src/helpers/edge.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { getRequiredServerFiles, NextConfig } from './config'
1616
import { getPluginVersion } from './functionsMetaData'
1717
import { makeLocaleOptional, stripLookahead, transformCaptureGroups } from './matchers'
1818
import { RoutesManifest } from './types'
19+
1920
// This is the format as of [email protected]
2021
interface EdgeFunctionDefinitionV1 {
2122
env: string[]
@@ -38,7 +39,7 @@ export interface MiddlewareMatcher {
3839

3940
// This is the format after [email protected]
4041
interface EdgeFunctionDefinitionV2 {
41-
env: string[]
42+
env?: string[]
4243
files: string[]
4344
name: string
4445
page: string
@@ -376,7 +377,6 @@ export const writeEdgeFunctions = async ({
376377
const { publish } = netlifyConfig.build
377378
const nextConfigFile = await getRequiredServerFiles(publish)
378379
const nextConfig = nextConfigFile.config
379-
const usesAppDir = nextConfig.experimental?.appDir
380380

381381
await copy(getEdgeTemplatePath('../vendor'), join(edgeFunctionRoot, 'vendor'))
382382
await copy(getEdgeTemplatePath('../edge-shared'), join(edgeFunctionRoot, 'edge-shared'))
@@ -462,8 +462,7 @@ export const writeEdgeFunctions = async ({
462462
function: functionName,
463463
name: edgeFunctionDefinition.name,
464464
pattern,
465-
// cache: "manual" is currently experimental, so we restrict it to sites that use experimental appDir
466-
cache: usesAppDir ? 'manual' : undefined,
465+
cache: 'manual',
467466
generator,
468467
})
469468
// pages-dir page routes also have a data route. If there's a match, add an entry mapping that to the function too
@@ -473,7 +472,7 @@ export const writeEdgeFunctions = async ({
473472
function: functionName,
474473
name: edgeFunctionDefinition.name,
475474
pattern: dataRoute,
476-
cache: usesAppDir ? 'manual' : undefined,
475+
cache: 'manual',
477476
generator,
478477
})
479478
}

packages/runtime/src/helpers/functions.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { getResolverForPages, getResolverForSourceFiles } from '../templates/get
2828
import { ApiConfig, extractConfigFromFile, isEdgeConfig } from './analysis'
2929
import { getRequiredServerFiles } from './config'
3030
import { getDependenciesOfFile, getServerFile, getSourceFileForPage } from './files'
31-
import { writeFunctionConfiguration } from './functionsMetaData'
31+
import { writeFunctionConfiguration, useRequireHooks } from './functionsMetaData'
3232
import { pack } from './pack'
3333
import { ApiRouteType } from './types'
3434
import { getFunctionNameForPage } from './utils'
@@ -132,11 +132,13 @@ export const generateFunctions = async (
132132
}
133133

134134
const writeHandler = async (functionName: string, functionTitle: string, isODB: boolean) => {
135+
const useHooks = await useRequireHooks()
135136
const handlerSource = getHandler({
136137
isODB,
137138
publishDir,
138139
appDir: relative(functionDir, appDir),
139140
nextServerModuleRelativeLocation,
141+
useHooks,
140142
})
141143
await ensureDir(join(functionsDir, functionName))
142144

packages/runtime/src/helpers/functionsMetaData.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { existsSync, readJSON, writeFile } from 'fs-extra'
22
import { join } from 'pathe'
3+
import { satisfies } from 'semver'
34

45
import { NEXT_PLUGIN, NEXT_PLUGIN_NAME } from '../constants'
56

@@ -17,8 +18,8 @@ const getNextRuntimeVersion = async (packageJsonPath: string, useNodeModulesPath
1718

1819
const PLUGIN_PACKAGE_PATH = '.netlify/plugins/package.json'
1920

20-
const nextPluginVersion = async () => {
21-
const moduleRoot = resolveModuleRoot(NEXT_PLUGIN)
21+
const nextPluginVersion = async (module?: string) => {
22+
const moduleRoot = resolveModuleRoot(module || NEXT_PLUGIN)
2223
const nodeModulesPath = moduleRoot ? join(moduleRoot, 'package.json') : null
2324

2425
return (
@@ -31,6 +32,8 @@ const nextPluginVersion = async () => {
3132

3233
export const getPluginVersion = async () => `${NEXT_PLUGIN_NAME}@${await nextPluginVersion()}`
3334

35+
export const useRequireHooks = async () => satisfies(await nextPluginVersion('next'), '13.3.3 - 13.4.9')
36+
3437
// The information needed to create a function configuration file
3538
export interface FunctionInfo {
3639
// The name of the function, e.g. `___netlify-handler`

packages/runtime/src/helpers/utils.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ export const getFunctionNameForPage = (page: string, background = false) =>
2424
.replace(DYNAMIC_PARAMETER_REGEX, '_$1-PARAM')
2525
.replace(RESERVED_FILENAME, '_')}-${background ? 'background' : 'handler'}`
2626

27-
type ExperimentalConfigWithLegacy = ExperimentalConfig & {
27+
export type ExperimentalConfigWithLegacy = ExperimentalConfig & {
2828
images?: Pick<ImageConfigComplete, 'remotePatterns'>
29+
appDir?: boolean
2930
}
3031

3132
export const toNetlifyRoute = (nextRoute: string): Array<string> => {

packages/runtime/src/index.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ import {
3131
getSSRLambdas,
3232
} from './helpers/functions'
3333
import { generateRedirects, generateStaticRedirects } from './helpers/redirects'
34-
import { shouldSkip, isNextAuthInstalled, getCustomImageResponseHeaders, getRemotePatterns } from './helpers/utils'
34+
import {
35+
shouldSkip,
36+
isNextAuthInstalled,
37+
getCustomImageResponseHeaders,
38+
getRemotePatterns,
39+
ExperimentalConfigWithLegacy,
40+
} from './helpers/utils'
3541
import {
3642
verifyNetlifyBuildVersion,
3743
checkNextSiteHasBuilt,
@@ -275,7 +281,11 @@ const plugin: NetlifyPlugin = {
275281
await checkZipSize(join(FUNCTIONS_DIST, `${ODB_FUNCTION_NAME}.zip`))
276282
const nextConfig = await getNextConfig({ publish, failBuild })
277283

278-
const { basePath, appDir, experimental } = nextConfig
284+
const {
285+
basePath,
286+
appDir,
287+
experimental,
288+
}: { basePath: string; appDir?: string; experimental: ExperimentalConfigWithLegacy } = nextConfig
279289

280290
generateCustomHeaders(nextConfig, headers)
281291

packages/runtime/src/templates/getHandler.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Bridge as NodeBridge } from '@vercel/node-bridge/bridge'
44
import { outdent as javascript } from 'outdent'
55

66
import type { NextConfig } from '../helpers/config'
7+
import { ExperimentalConfigWithLegacy } from '../helpers/utils'
78

89
import type { NextServerType } from './handlerUtils'
910
import type { NetlifyNextServerType } from './server'
@@ -37,6 +38,7 @@ type MakeHandlerParams = {
3738
staticManifest: Array<[string, string]>
3839
blobsManifest: Set<string>
3940
mode: 'ssr' | 'odb'
41+
useHooks: boolean
4042
}
4143

4244
// We return a function and then call `toString()` on it to serialise it as the launcher function
@@ -49,6 +51,7 @@ const makeHandler = ({
4951
staticManifest = [],
5052
blobsManifest = new Set(),
5153
mode = 'ssr',
54+
useHooks,
5255
}: MakeHandlerParams) => {
5356
// Change working directory into the site root, unless using Nx, which moves the
5457
// dist directory and handles this itself
@@ -63,10 +66,13 @@ const makeHandler = ({
6366
require.resolve('./pages.js')
6467
} catch {}
6568

69+
const { appDir }: ExperimentalConfigWithLegacy = conf.experimental
6670
// Next 13.4 conditionally uses different React versions and we need to make sure we use the same one
67-
overrideRequireHooks(conf)
71+
// With the release of 13.5 experimental.appDir is no longer used.
72+
// we will need to check if appDir is set and Next version before running requireHooks
73+
if (appDir && useHooks) overrideRequireHooks(conf.experimental)
6874
const NetlifyNextServer: NetlifyNextServerType = getNetlifyNextServer(NextServer)
69-
applyRequireHooks()
75+
if (appDir && useHooks) applyRequireHooks()
7076

7177
const ONE_YEAR_IN_SECONDS = 31536000
7278

@@ -224,6 +230,7 @@ export const getHandler = ({
224230
publishDir = '../../../.next',
225231
appDir = '../../..',
226232
nextServerModuleRelativeLocation,
233+
useHooks,
227234
}): string =>
228235
// This is a string, but if you have the right editor plugin it should format as js (e.g. bierner.comment-tagged-templates in VS Code)
229236
javascript/* javascript */ `
@@ -239,7 +246,7 @@ export const getHandler = ({
239246
const { setBlobInit } = require('./blobStorage')
240247
// We copy the file here rather than requiring from the node module
241248
const { Bridge } = require("./bridge");
242-
const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, normalizePath } = require('./handlerUtils')
249+
const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, normalizePath, nextVersionNum } = require('./handlerUtils')
243250
const { overrideRequireHooks, applyRequireHooks } = require("./requireHooks")
244251
const { getNetlifyNextServer } = require("./server")
245252
const NextServer = require(${JSON.stringify(nextServerModuleRelativeLocation)}).default
@@ -257,7 +264,7 @@ export const getHandler = ({
257264
const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", "server"));
258265
exports.handler = ${
259266
isODB
260-
? `builder((${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, blobsManifest, mode: 'odb' }));`
261-
: `(${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, blobsManifest, mode: 'ssr' });`
267+
? `builder((${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, blobsManifest, mode: 'odb', useHooks: ${useHooks}}));`
268+
: `(${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, blobsManifest, mode: 'ssr', useHooks: ${useHooks}});`
262269
}
263270
`

packages/runtime/src/templates/requireHooks.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55

66
import mod from 'module'
77

8-
import type { NextConfig } from '../helpers/config'
8+
import type { ExperimentalConfigWithLegacy } from '../helpers/utils'
99

1010
const resolveFilename = (mod as any)._resolveFilename
1111
const requireHooks = new Map<string, Map<string, string>>()
1212

13-
export const overrideRequireHooks = (config: NextConfig) => {
14-
setRequireHooks(config)
13+
export const overrideRequireHooks = (experimental: ExperimentalConfigWithLegacy) => {
14+
setRequireHooks(experimental)
1515
resolveRequireHooks()
1616
}
1717

18-
const setRequireHooks = (config: NextConfig) => {
18+
const setRequireHooks = (experimental: ExperimentalConfigWithLegacy) => {
1919
requireHooks.set(
2020
'default',
2121
new Map([
@@ -24,8 +24,8 @@ const setRequireHooks = (config: NextConfig) => {
2424
]),
2525
)
2626

27-
if (config.experimental.appDir) {
28-
if (config.experimental.serverActions) {
27+
if (experimental.appDir) {
28+
if (experimental.serverActions) {
2929
requireHooks.set(
3030
'experimental',
3131
new Map([

packages/runtime/src/templates/server.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import type { PrerenderManifest } from 'next/dist/build'
66
import type { BaseNextResponse } from 'next/dist/server/base-http'
77
import type { NodeRequestHandler, Options } from 'next/dist/server/next-server'
88

9+
import { ExperimentalConfigWithLegacy } from '../helpers/utils'
10+
911
import {
1012
netlifyApiFetch,
1113
NextServerType,
@@ -19,6 +21,9 @@ import {
1921
interface NetlifyConfig {
2022
revalidateToken?: string
2123
}
24+
interface NextConfigWithAppDir extends NextConfig {
25+
experimental: ExperimentalConfigWithLegacy
26+
}
2227

2328
// eslint-disable-next-line max-lines-per-function
2429
const getNetlifyNextServer = (NextServer: NextServerType) => {
@@ -75,7 +80,9 @@ const getNetlifyNextServer = (NextServer: NextServerType) => {
7580
const { url, headers } = req
7681

7782
// conditionally use the prebundled React module
78-
this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl)
83+
// PrebundledReact should only apply when appDir is set it falls between the specified Next versions
84+
const { experimental }: NextConfigWithAppDir = this.nextConfig
85+
if (experimental?.appDir) this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl)
7986

8087
// intercept on-demand revalidation requests and handle with the Netlify API
8188
if (headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) {

test/index.spec.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ describe('onBuild()', () => {
455455
`!node_modules/next/dist/next-server/server/lib/squoosh/**/*.wasm`,
456456
'!node_modules/next/dist/compiled/webpack/bundle4.js',
457457
'!node_modules/next/dist/compiled/webpack/bundle5.js',
458+
'node_modules/styled-jsx/style.js',
458459
'!node_modules/sharp/**/*',
459460
]
460461
// Relative paths in Windows are different
@@ -558,10 +559,10 @@ describe('onBuild()', () => {
558559
expect(existsSync(odbHandlerFile)).toBeTruthy()
559560

560561
expect(readFileSync(handlerFile, 'utf8')).toMatch(
561-
`({ conf: config, app: "../../..", pageRoot, NextServer, staticManifest, blobsManifest, mode: 'ssr' })`,
562+
`({ conf: config, app: "../../..", pageRoot, NextServer, staticManifest, blobsManifest, mode: 'ssr', useHooks: false})`,
562563
)
563564
expect(readFileSync(odbHandlerFile, 'utf8')).toMatch(
564-
`({ conf: config, app: "../../..", pageRoot, NextServer, staticManifest, blobsManifest, mode: 'odb' })`,
565+
`({ conf: config, app: "../../..", pageRoot, NextServer, staticManifest, blobsManifest, mode: 'odb', useHooks: false})`,
565566
)
566567
expect(readFileSync(handlerFile, 'utf8')).toMatch(`require("../../../.next/required-server-files.json")`)
567568
expect(readFileSync(odbHandlerFile, 'utf8')).toMatch(`require("../../../.next/required-server-files.json")`)

test/templates/server.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ describe('the netlify next server', () => {
296296
await requestHandler(new NodeNextRequest(mockReq), new NodeNextResponse(mockRes))
297297

298298
// eslint-disable-next-line no-underscore-dangle
299-
expect(process.env.__NEXT_PRIVATE_PREBUNDLED_REACT).toBe('')
299+
expect(process.env.__NEXT_PRIVATE_PREBUNDLED_REACT).toBeFalsy()
300300
})
301301

302302
it('resolves the prebundled react version for app routes', async () => {

test/test-utils.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import path, { dirname } from 'path'
22

33
import cpy from 'cpy'
4-
import { writeJSON, existsSync, ensureDir, readJson, copy } from 'fs-extra'
4+
import { writeJSON, writeFile, existsSync, ensureDir, readJson, copy } from 'fs-extra'
55
import { dir as getTmpDir } from 'tmp-promise'
66

77
const FIXTURES_DIR = `${__dirname}/fixtures`
@@ -26,7 +26,7 @@ const rewriteAppDir = async function (dir = '.next') {
2626

2727
// Move .next from sample project to current directory
2828
export const moveNextDist = async function (dir = '.next', copyMods = false) {
29-
await (copyMods ? copyModules(['next', 'sharp']) : stubModules(['next', 'sharp']))
29+
await (copyMods ? copyModules(['next', 'sharp', 'styled-jsx']) : stubModules(['next', 'sharp', 'styled-jsx']))
3030
await ensureDir(dirname(dir))
3131
await copy(path.join(SAMPLE_PROJECT_DIR, '.next'), path.join(process.cwd(), dir))
3232

@@ -53,6 +53,9 @@ export const stubModules = async function (modules) {
5353
const dir = path.join(process.cwd(), 'node_modules', mod)
5454
await ensureDir(dir)
5555
await writeJSON(path.join(dir, 'package.json'), { name: mod })
56+
if (mod === `styled-jsx`) {
57+
await writeFile('style.js', '')
58+
}
5659
}
5760
}
5861

0 commit comments

Comments
 (0)