From 276f327c7cc7ac53f04de0cef34ea469a16dfcb4 Mon Sep 17 00:00:00 2001 From: Tatyana Date: Thu, 5 Oct 2023 16:35:35 -0500 Subject: [PATCH 01/21] fix: make sure appDir exists before running requireHooks --- packages/runtime/src/helpers/utils.ts | 3 ++- packages/runtime/src/templates/getHandler.ts | 8 ++++++-- packages/runtime/src/templates/requireHooks.ts | 12 ++++++------ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/runtime/src/helpers/utils.ts b/packages/runtime/src/helpers/utils.ts index dd33cb824b..bd473dce5b 100644 --- a/packages/runtime/src/helpers/utils.ts +++ b/packages/runtime/src/helpers/utils.ts @@ -24,8 +24,9 @@ export const getFunctionNameForPage = (page: string, background = false) => .replace(DYNAMIC_PARAMETER_REGEX, '_$1-PARAM') .replace(RESERVED_FILENAME, '_')}-${background ? 'background' : 'handler'}` -type ExperimentalConfigWithLegacy = ExperimentalConfig & { +export type ExperimentalConfigWithLegacy = ExperimentalConfig & { images?: Pick + appDir?: boolean } export const toNetlifyRoute = (nextRoute: string): Array => { diff --git a/packages/runtime/src/templates/getHandler.ts b/packages/runtime/src/templates/getHandler.ts index 8baadbb4fb..4aefbcd0d3 100644 --- a/packages/runtime/src/templates/getHandler.ts +++ b/packages/runtime/src/templates/getHandler.ts @@ -4,6 +4,7 @@ import type { Bridge as NodeBridge } from '@vercel/node-bridge/bridge' import { outdent as javascript } from 'outdent' import type { NextConfig } from '../helpers/config' +import { ExperimentalConfigWithLegacy } from '../helpers/utils' import type { NextServerType } from './handlerUtils' import type { NetlifyNextServerType } from './server' @@ -57,10 +58,13 @@ const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mod require.resolve('./pages.js') } catch {} + const { appDir }: ExperimentalConfigWithLegacy = conf.experimental // Next 13.4 conditionally uses different React versions and we need to make sure we use the same one - overrideRequireHooks(conf) + // With the release of 13.5 experimental.appDir is no longer used. + // we will need to check if appDir exists to run requireHooks for older versions + if (appDir) overrideRequireHooks(conf.experimental) const NetlifyNextServer: NetlifyNextServerType = getNetlifyNextServer(NextServer) - applyRequireHooks() + if (appDir) applyRequireHooks() const ONE_YEAR_IN_SECONDS = 31536000 diff --git a/packages/runtime/src/templates/requireHooks.ts b/packages/runtime/src/templates/requireHooks.ts index 6a917f90a1..a7c3d8fe21 100644 --- a/packages/runtime/src/templates/requireHooks.ts +++ b/packages/runtime/src/templates/requireHooks.ts @@ -5,17 +5,17 @@ import mod from 'module' -import type { NextConfig } from '../helpers/config' +import type { ExperimentalConfigWithLegacy } from '../helpers/utils' const resolveFilename = (mod as any)._resolveFilename const requireHooks = new Map>() -export const overrideRequireHooks = (config: NextConfig) => { - setRequireHooks(config) +export const overrideRequireHooks = (experimental: ExperimentalConfigWithLegacy) => { + setRequireHooks(experimental) resolveRequireHooks() } -const setRequireHooks = (config: NextConfig) => { +const setRequireHooks = (experimental: ExperimentalConfigWithLegacy) => { requireHooks.set( 'default', new Map([ @@ -24,8 +24,8 @@ const setRequireHooks = (config: NextConfig) => { ]), ) - if (config.experimental.appDir) { - if (config.experimental.serverActions) { + if (experimental.appDir) { + if (experimental.serverActions) { requireHooks.set( 'experimental', new Map([ From eb887c04c5e3a02b034fd167c0802cfad3e95fab Mon Sep 17 00:00:00 2001 From: Tatyana Date: Thu, 5 Oct 2023 18:52:12 -0500 Subject: [PATCH 02/21] fix: run prebundledReact if appDir exists in conf --- packages/runtime/src/templates/server.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/runtime/src/templates/server.ts b/packages/runtime/src/templates/server.ts index 3c2ced2c39..63bdab56df 100644 --- a/packages/runtime/src/templates/server.ts +++ b/packages/runtime/src/templates/server.ts @@ -15,6 +15,8 @@ import { unlocalizeRoute, getMatchedRoute, } from './handlerUtils' +import { join } from 'path' +import { ExperimentalConfigWithLegacy } from '../helpers/utils' interface NetlifyConfig { revalidateToken?: string @@ -30,11 +32,16 @@ const getNetlifyNextServer = (NextServer: NextServerType) => { return this.nextConfig.experimental?.serverActions ? 'experimental' : 'next' } + protected getManifest(manifest: string) { + // eslint-disable-next-line import/no-dynamic-require + return require(join(this.distDir, manifest)) + } + public constructor(options: Options, netlifyConfig: NetlifyConfig) { super(options) this.netlifyConfig = netlifyConfig // copy the prerender manifest so it doesn't get mutated by Next.js - const manifest = this.getPrerenderManifest() + const manifest = this.getPrerenderManifest() || this.getManifest('prerender-manifest.json') this.netlifyPrerenderManifest = { ...manifest, routes: { ...manifest.routes }, @@ -53,7 +60,8 @@ const getNetlifyNextServer = (NextServer: NextServerType) => { const { url, headers } = req // conditionally use the prebundled React module - this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl) + const { appDir }: ExperimentalConfigWithLegacy = this.nextConfig.experimental + if (appDir) this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl) // intercept on-demand revalidation requests and handle with the Netlify API if (headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) { @@ -83,12 +91,12 @@ const getNetlifyNextServer = (NextServer: NextServerType) => { // doing what they do in https://github.com/vercel/vercel/blob/1663db7ca34d3dd99b57994f801fb30b72fbd2f3/packages/next/src/server-build.ts#L576-L580 private async netlifyPrebundleReact(path: string, { basePath, trailingSlash }: NextConfig, parsedUrl) { - const routesManifest = this.getRoutesManifest?.() + const routesManifest = this.getRoutesManifest?.() || this.getManifest('routes-manifest.json') const appPathsRoutes = this.getAppPathRoutes?.() const routes = routesManifest && [...routesManifest.staticRoutes, ...routesManifest.dynamicRoutes] const matchedRoute = await getMatchedRoute(path, routes, parsedUrl, basePath, trailingSlash) const isAppRoute = appPathsRoutes && matchedRoute ? appPathsRoutes[matchedRoute.page] : false - + if (isAppRoute) { // app routes should use prebundled React // eslint-disable-next-line no-underscore-dangle From ee7e8fcfd187256420c2458d6a92458616138d6a Mon Sep 17 00:00:00 2001 From: Tatyana Date: Thu, 5 Oct 2023 18:53:15 -0500 Subject: [PATCH 03/21] fix: use ExperimentalConfigWithLegacy for appDir --- packages/runtime/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index b17dd2db9d..c999a94d02 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -31,7 +31,7 @@ import { getSSRLambdas, } from './helpers/functions' import { generateRedirects, generateStaticRedirects } from './helpers/redirects' -import { shouldSkip, isNextAuthInstalled, getCustomImageResponseHeaders, getRemotePatterns } from './helpers/utils' +import { shouldSkip, isNextAuthInstalled, getCustomImageResponseHeaders, getRemotePatterns, ExperimentalConfigWithLegacy } from './helpers/utils' import { verifyNetlifyBuildVersion, checkNextSiteHasBuilt, @@ -248,7 +248,7 @@ const plugin: NetlifyPlugin = { await checkZipSize(join(FUNCTIONS_DIST, `${ODB_FUNCTION_NAME}.zip`)) const nextConfig = await getNextConfig({ publish, failBuild }) - const { basePath, appDir, experimental } = nextConfig + const { basePath, appDir, experimental } : {basePath: string, appDir?: string, experimental: ExperimentalConfigWithLegacy} = nextConfig generateCustomHeaders(nextConfig, headers) From 4b6cf199ea3823f63b2146ada87edc51426276b0 Mon Sep 17 00:00:00 2001 From: Tatyana Date: Thu, 5 Oct 2023 18:54:28 -0500 Subject: [PATCH 04/21] fix: set cache to manual and env as optional --- packages/runtime/src/helpers/edge.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/runtime/src/helpers/edge.ts b/packages/runtime/src/helpers/edge.ts index dadc6da9bc..895c501922 100644 --- a/packages/runtime/src/helpers/edge.ts +++ b/packages/runtime/src/helpers/edge.ts @@ -16,6 +16,7 @@ import { getRequiredServerFiles, NextConfig } from './config' import { getPluginVersion } from './functionsMetaData' import { makeLocaleOptional, stripLookahead, transformCaptureGroups } from './matchers' import { RoutesManifest } from './types' + // This is the format as of next@12.2 interface EdgeFunctionDefinitionV1 { env: string[] @@ -38,7 +39,7 @@ export interface MiddlewareMatcher { // This is the format after next@12.3.0 interface EdgeFunctionDefinitionV2 { - env: string[] + env?: string[] files: string[] name: string page: string @@ -376,7 +377,6 @@ export const writeEdgeFunctions = async ({ const { publish } = netlifyConfig.build const nextConfigFile = await getRequiredServerFiles(publish) const nextConfig = nextConfigFile.config - const usesAppDir = nextConfig.experimental?.appDir await copy(getEdgeTemplatePath('../vendor'), join(edgeFunctionRoot, 'vendor')) await copy(getEdgeTemplatePath('../edge-shared'), join(edgeFunctionRoot, 'edge-shared')) @@ -463,7 +463,7 @@ export const writeEdgeFunctions = async ({ name: edgeFunctionDefinition.name, pattern, // cache: "manual" is currently experimental, so we restrict it to sites that use experimental appDir - cache: usesAppDir ? 'manual' : undefined, + cache: 'manual', generator, }) // 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 +473,7 @@ export const writeEdgeFunctions = async ({ function: functionName, name: edgeFunctionDefinition.name, pattern: dataRoute, - cache: usesAppDir ? 'manual' : undefined, + cache: 'manual', generator, }) } From 67721f341deab951322fe3f1f58dac147a72c142 Mon Sep 17 00:00:00 2001 From: Tatyana Date: Thu, 5 Oct 2023 20:08:13 -0500 Subject: [PATCH 05/21] fix: prettier --- packages/runtime/src/index.ts | 14 ++++++++++++-- packages/runtime/src/templates/server.ts | 7 ++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index c999a94d02..a69628fbee 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -31,7 +31,13 @@ import { getSSRLambdas, } from './helpers/functions' import { generateRedirects, generateStaticRedirects } from './helpers/redirects' -import { shouldSkip, isNextAuthInstalled, getCustomImageResponseHeaders, getRemotePatterns, ExperimentalConfigWithLegacy } from './helpers/utils' +import { + shouldSkip, + isNextAuthInstalled, + getCustomImageResponseHeaders, + getRemotePatterns, + ExperimentalConfigWithLegacy, +} from './helpers/utils' import { verifyNetlifyBuildVersion, checkNextSiteHasBuilt, @@ -248,7 +254,11 @@ const plugin: NetlifyPlugin = { await checkZipSize(join(FUNCTIONS_DIST, `${ODB_FUNCTION_NAME}.zip`)) const nextConfig = await getNextConfig({ publish, failBuild }) - const { basePath, appDir, experimental } : {basePath: string, appDir?: string, experimental: ExperimentalConfigWithLegacy} = nextConfig + const { + basePath, + appDir, + experimental, + }: { basePath: string; appDir?: string; experimental: ExperimentalConfigWithLegacy } = nextConfig generateCustomHeaders(nextConfig, headers) diff --git a/packages/runtime/src/templates/server.ts b/packages/runtime/src/templates/server.ts index 63bdab56df..4815c62418 100644 --- a/packages/runtime/src/templates/server.ts +++ b/packages/runtime/src/templates/server.ts @@ -1,3 +1,4 @@ +import { join } from 'path' // eslint-disable-next-line n/no-deprecated-api -- this is what Next.js uses as well import { parse } from 'url' @@ -6,6 +7,8 @@ import type { PrerenderManifest } from 'next/dist/build' import type { BaseNextResponse } from 'next/dist/server/base-http' import type { NodeRequestHandler, Options } from 'next/dist/server/next-server' +import { ExperimentalConfigWithLegacy } from '../helpers/utils' + import { netlifyApiFetch, NextServerType, @@ -15,8 +18,6 @@ import { unlocalizeRoute, getMatchedRoute, } from './handlerUtils' -import { join } from 'path' -import { ExperimentalConfigWithLegacy } from '../helpers/utils' interface NetlifyConfig { revalidateToken?: string @@ -96,7 +97,7 @@ const getNetlifyNextServer = (NextServer: NextServerType) => { const routes = routesManifest && [...routesManifest.staticRoutes, ...routesManifest.dynamicRoutes] const matchedRoute = await getMatchedRoute(path, routes, parsedUrl, basePath, trailingSlash) const isAppRoute = appPathsRoutes && matchedRoute ? appPathsRoutes[matchedRoute.page] : false - + if (isAppRoute) { // app routes should use prebundled React // eslint-disable-next-line no-underscore-dangle From 730fb3e1799109681982f44e7db9fde7a0922a2c Mon Sep 17 00:00:00 2001 From: Tatyana Date: Thu, 5 Oct 2023 20:32:32 -0500 Subject: [PATCH 06/21] fix: update appDir check --- packages/runtime/src/templates/server.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/runtime/src/templates/server.ts b/packages/runtime/src/templates/server.ts index 4815c62418..f21002ca70 100644 --- a/packages/runtime/src/templates/server.ts +++ b/packages/runtime/src/templates/server.ts @@ -22,6 +22,9 @@ import { interface NetlifyConfig { revalidateToken?: string } +interface NextConfigWithAppDir extends NextConfig { + experimental: ExperimentalConfigWithLegacy +} // eslint-disable-next-line max-lines-per-function const getNetlifyNextServer = (NextServer: NextServerType) => { @@ -61,8 +64,8 @@ const getNetlifyNextServer = (NextServer: NextServerType) => { const { url, headers } = req // conditionally use the prebundled React module - const { appDir }: ExperimentalConfigWithLegacy = this.nextConfig.experimental - if (appDir) this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl) + const { experimental }: NextConfigWithAppDir = this.nextConfig + if (experimental?.appDir) this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl) // intercept on-demand revalidation requests and handle with the Netlify API if (headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) { From 0ae9034c111b527d5382bcef5f784eed845a8cac Mon Sep 17 00:00:00 2001 From: Tatyana Date: Thu, 5 Oct 2023 21:11:04 -0500 Subject: [PATCH 07/21] fix: styled-jsx solution for 13.5 --- packages/runtime/src/helpers/config.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/runtime/src/helpers/config.ts b/packages/runtime/src/helpers/config.ts index 8db64bdbbf..10717ca3a8 100644 --- a/packages/runtime/src/helpers/config.ts +++ b/packages/runtime/src/helpers/config.ts @@ -1,3 +1,5 @@ +import mod from 'module' + import type { NetlifyConfig } from '@netlify/build/types' import destr from 'destr' import { readJSON, writeJSON } from 'fs-extra' @@ -164,6 +166,17 @@ export const configureHandlerFunctions = async ({ ) } + try { + // on Next 13.5+ there is no longer statically analyzable import to styled-jsx/style + // so lambda fails to bundle it. Next require hooks actually try to resolve it + // and fail if it is not bundled, so we forcefully add it to lambda. + + // eslint-disable-next-line n/no-unsupported-features/node-builtins + const nextRequire = mod.createRequire(require.resolve(`next`)) + const styledJsxPath = nextRequire.resolve(`styled-jsx/style`) + netlifyConfig.functions[functionName].included_files.push(styledJsxPath) + } catch {} + excludedModules.forEach((moduleName) => { const moduleRoot = resolveModuleRoot(moduleName) if (moduleRoot) { From e270785a9c7b5ae291381865eb5f36733d75e343 Mon Sep 17 00:00:00 2001 From: Tatyana Date: Thu, 5 Oct 2023 23:45:25 -0500 Subject: [PATCH 08/21] test: add styled-jsx to included files test --- test/index.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/index.spec.ts b/test/index.spec.ts index f80aa19f6b..def83d27d1 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -455,6 +455,7 @@ describe('onBuild()', () => { `!node_modules/next/dist/next-server/server/lib/squoosh/**/*.wasm`, '!node_modules/next/dist/compiled/webpack/bundle4.js', '!node_modules/next/dist/compiled/webpack/bundle5.js', + '/home/runner/work/next-runtime/next-runtime/node_modules/styled-jsx/style.js', '!node_modules/sharp/**/*', ] // Relative paths in Windows are different From 4f424e57010ae84fe2b339b3853ffc791814b3cd Mon Sep 17 00:00:00 2001 From: Tatyana Date: Thu, 5 Oct 2023 23:47:45 -0500 Subject: [PATCH 09/21] test: added next latest to i18n for testing --- demos/next-i18next/package.json | 2 +- package.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/demos/next-i18next/package.json b/demos/next-i18next/package.json index 713b65adf7..a94f8097dd 100644 --- a/demos/next-i18next/package.json +++ b/demos/next-i18next/package.json @@ -9,7 +9,7 @@ "lint": "next lint" }, "dependencies": { - "next": "^13.4.1", + "next": "^13.5.4", "next-i18next": "^11.0.0", "react": "^18.2.0", "react-dom": "^18.2.0" diff --git a/package.json b/package.json index 5e6bb1731d..45a63977ab 100644 --- a/package.json +++ b/package.json @@ -142,6 +142,7 @@ "demos/server-components", "demos/middleware", "demos/custom-routes", - "demos/next-with-edge-functions" + "demos/next-with-edge-functions", + "demos/next-i18next" ] } From 5f0869006d0f800d7d00988a5a537db3c24304da Mon Sep 17 00:00:00 2001 From: Tatyana Date: Fri, 6 Oct 2023 00:02:04 -0500 Subject: [PATCH 10/21] fix: remove i18n for wp --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 45a63977ab..5e6bb1731d 100644 --- a/package.json +++ b/package.json @@ -142,7 +142,6 @@ "demos/server-components", "demos/middleware", "demos/custom-routes", - "demos/next-with-edge-functions", - "demos/next-i18next" + "demos/next-with-edge-functions" ] } From ec9698c3594fd0eaae43f194f1203725339a75be Mon Sep 17 00:00:00 2001 From: Tatyana Date: Mon, 9 Oct 2023 19:33:42 -0500 Subject: [PATCH 11/21] fix: use requireHooks based on next -v and appDir --- packages/runtime/src/templates/getHandler.ts | 14 +++++++---- .../runtime/src/templates/handlerUtils.ts | 23 ++++++++++++++++++- packages/runtime/src/templates/server.ts | 5 +++- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/runtime/src/templates/getHandler.ts b/packages/runtime/src/templates/getHandler.ts index 4aefbcd0d3..12bd997e62 100644 --- a/packages/runtime/src/templates/getHandler.ts +++ b/packages/runtime/src/templates/getHandler.ts @@ -24,6 +24,7 @@ const { getMultiValueHeaders, getPrefetchResponse, normalizePath, + nextVersionNum, } = require('./handlerUtils') const { overrideRequireHooks, applyRequireHooks } = require('./requireHooks') const { getNetlifyNextServer } = require('./server') @@ -61,10 +62,15 @@ const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mod const { appDir }: ExperimentalConfigWithLegacy = conf.experimental // Next 13.4 conditionally uses different React versions and we need to make sure we use the same one // With the release of 13.5 experimental.appDir is no longer used. - // we will need to check if appDir exists to run requireHooks for older versions - if (appDir) overrideRequireHooks(conf.experimental) + // we will need to check if appDir is set and Next version before running requireHooks + const runRequireHooks = async (hook) => + await nextVersionNum() + .then((version) => (appDir && version ? hook : null)) + .catch(() => ({})) + + runRequireHooks(overrideRequireHooks(conf.experimental)) const NetlifyNextServer: NetlifyNextServerType = getNetlifyNextServer(NextServer) - if (appDir) applyRequireHooks() + runRequireHooks(applyRequireHooks()) const ONE_YEAR_IN_SECONDS = 31536000 @@ -222,7 +228,7 @@ export const getHandler = ({ const { promises } = require("fs"); // We copy the file here rather than requiring from the node module const { Bridge } = require("./bridge"); - const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, normalizePath } = require('./handlerUtils') + const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, normalizePath, nextVersionNum } = require('./handlerUtils') const { overrideRequireHooks, applyRequireHooks } = require("./requireHooks") const { getNetlifyNextServer } = require("./server") const NextServer = require(${JSON.stringify(nextServerModuleRelativeLocation)}).default diff --git a/packages/runtime/src/templates/handlerUtils.ts b/packages/runtime/src/templates/handlerUtils.ts index b905293220..65bedfc0b0 100644 --- a/packages/runtime/src/templates/handlerUtils.ts +++ b/packages/runtime/src/templates/handlerUtils.ts @@ -1,13 +1,15 @@ import fs, { createWriteStream, existsSync } from 'fs' import { ServerResponse } from 'http' import { tmpdir } from 'os' -import path from 'path' +import path, { dirname, join, relative } from 'path' import { pipeline } from 'stream' import { promisify } from 'util' import { HandlerEvent, HandlerResponse } from '@netlify/functions' import { http, https } from 'follow-redirects' +import { readJSON } from 'fs-extra' import NextNodeServer from 'next/dist/server/next-server' +import { satisfies } from 'semver' import type { StaticRoute } from '../helpers/types' @@ -274,6 +276,25 @@ export const localizeDataRoute = (dataRoute: string, localizedRoute: string): st .replace(/\/index\.json$/, '.json') } +export const resolveModuleRoot = (moduleName) => { + try { + return dirname(relative(process.cwd(), require.resolve(`${moduleName}/package.json`, { paths: [process.cwd()] }))) + } catch { + return null + } +} +// Had to copy nextPluginVersion logic from functionsMetaData for it to work within server.ts and getHandler.ts +const nextPluginVersion = async (module: string) => { + const moduleRoot = resolveModuleRoot(module) + if (!existsSync(moduleRoot)) { + return + } + const packagePlugin = await readJSON(join(moduleRoot, 'package.json')) + return packagePlugin?.version +} + +export const nextVersionNum = async () => satisfies(await nextPluginVersion('next'), '13.3.3 - 13.4.9') + export const getMatchedRoute = ( paths: string, routesManifest: Array, diff --git a/packages/runtime/src/templates/server.ts b/packages/runtime/src/templates/server.ts index f21002ca70..bfeb884cbe 100644 --- a/packages/runtime/src/templates/server.ts +++ b/packages/runtime/src/templates/server.ts @@ -17,6 +17,7 @@ import { localizeDataRoute, unlocalizeRoute, getMatchedRoute, + nextVersionNum, } from './handlerUtils' interface NetlifyConfig { @@ -64,8 +65,10 @@ const getNetlifyNextServer = (NextServer: NextServerType) => { const { url, headers } = req // conditionally use the prebundled React module + // PrebundledReact should only apply when appDir is set it falls between the specified Next versions const { experimental }: NextConfigWithAppDir = this.nextConfig - if (experimental?.appDir) this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl) + const version = await nextVersionNum() + if (experimental?.appDir && version) this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl) // intercept on-demand revalidation requests and handle with the Netlify API if (headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) { From 8728b30970eaf3569651ba8edf599f8f7ce441cd Mon Sep 17 00:00:00 2001 From: Tatyana Date: Mon, 9 Oct 2023 20:21:51 -0500 Subject: [PATCH 12/21] fix: remove fs-extra --- packages/runtime/src/templates/handlerUtils.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/runtime/src/templates/handlerUtils.ts b/packages/runtime/src/templates/handlerUtils.ts index 65bedfc0b0..396591ba34 100644 --- a/packages/runtime/src/templates/handlerUtils.ts +++ b/packages/runtime/src/templates/handlerUtils.ts @@ -7,7 +7,6 @@ import { promisify } from 'util' import { HandlerEvent, HandlerResponse } from '@netlify/functions' import { http, https } from 'follow-redirects' -import { readJSON } from 'fs-extra' import NextNodeServer from 'next/dist/server/next-server' import { satisfies } from 'semver' @@ -284,16 +283,17 @@ export const resolveModuleRoot = (moduleName) => { } } // Had to copy nextPluginVersion logic from functionsMetaData for it to work within server.ts and getHandler.ts -const nextPluginVersion = async (module: string) => { - const moduleRoot = resolveModuleRoot(module) +const nextPluginVersion = async () => { + const moduleRoot = resolveModuleRoot('next') if (!existsSync(moduleRoot)) { return } - const packagePlugin = await readJSON(join(moduleRoot, 'package.json')) + const packagePath = join(moduleRoot, 'package.json') + const packagePlugin = await fs.promises.readFile(packagePath, 'utf-8').then((contents) => JSON.parse(contents)) return packagePlugin?.version } -export const nextVersionNum = async () => satisfies(await nextPluginVersion('next'), '13.3.3 - 13.4.9') +export const nextVersionNum = async () => satisfies(await nextPluginVersion(), '13.3.3 - 13.4.9') export const getMatchedRoute = ( paths: string, From ce20029bcca36ab9531c6d25192f2bbec97d8a99 Mon Sep 17 00:00:00 2001 From: Tatyana Date: Mon, 9 Oct 2023 20:55:08 -0500 Subject: [PATCH 13/21] fix: add semver to handler --- packages/runtime/src/templates/getHandler.ts | 4 +++- packages/runtime/src/templates/handlerUtils.ts | 3 +-- packages/runtime/src/templates/server.ts | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/runtime/src/templates/getHandler.ts b/packages/runtime/src/templates/getHandler.ts index 12bd997e62..87389e714f 100644 --- a/packages/runtime/src/templates/getHandler.ts +++ b/packages/runtime/src/templates/getHandler.ts @@ -17,6 +17,7 @@ const path = require('path') const { URLSearchParams, URL } = require('url') const { Bridge } = require('@vercel/node-bridge/bridge') +const { satisfies } = require('semver') const { augmentFsModule, @@ -64,7 +65,7 @@ const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mod // With the release of 13.5 experimental.appDir is no longer used. // we will need to check if appDir is set and Next version before running requireHooks const runRequireHooks = async (hook) => - await nextVersionNum() + await nextVersionNum(satisfies) .then((version) => (appDir && version ? hook : null)) .catch(() => ({})) @@ -226,6 +227,7 @@ export const getHandler = ({ const { Server } = require("http"); const { promises } = require("fs"); + const { satisfies } = require('semver') // We copy the file here rather than requiring from the node module const { Bridge } = require("./bridge"); const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, normalizePath, nextVersionNum } = require('./handlerUtils') diff --git a/packages/runtime/src/templates/handlerUtils.ts b/packages/runtime/src/templates/handlerUtils.ts index 396591ba34..f0d1d23c7e 100644 --- a/packages/runtime/src/templates/handlerUtils.ts +++ b/packages/runtime/src/templates/handlerUtils.ts @@ -8,7 +8,6 @@ import { promisify } from 'util' import { HandlerEvent, HandlerResponse } from '@netlify/functions' import { http, https } from 'follow-redirects' import NextNodeServer from 'next/dist/server/next-server' -import { satisfies } from 'semver' import type { StaticRoute } from '../helpers/types' @@ -293,7 +292,7 @@ const nextPluginVersion = async () => { return packagePlugin?.version } -export const nextVersionNum = async () => satisfies(await nextPluginVersion(), '13.3.3 - 13.4.9') +export const nextVersionNum = async (satisfies) => satisfies(await nextPluginVersion(), '13.3.3 - 13.4.9') export const getMatchedRoute = ( paths: string, diff --git a/packages/runtime/src/templates/server.ts b/packages/runtime/src/templates/server.ts index bfeb884cbe..59b5c26c28 100644 --- a/packages/runtime/src/templates/server.ts +++ b/packages/runtime/src/templates/server.ts @@ -6,6 +6,7 @@ import { NextConfig } from 'next' import type { PrerenderManifest } from 'next/dist/build' import type { BaseNextResponse } from 'next/dist/server/base-http' import type { NodeRequestHandler, Options } from 'next/dist/server/next-server' +import { satisfies } from 'semver' import { ExperimentalConfigWithLegacy } from '../helpers/utils' @@ -67,7 +68,7 @@ const getNetlifyNextServer = (NextServer: NextServerType) => { // conditionally use the prebundled React module // PrebundledReact should only apply when appDir is set it falls between the specified Next versions const { experimental }: NextConfigWithAppDir = this.nextConfig - const version = await nextVersionNum() + const version = await nextVersionNum(satisfies) if (experimental?.appDir && version) this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl) // intercept on-demand revalidation requests and handle with the Netlify API From 548a482b111cef1bff764228f908dbb0c5b775ef Mon Sep 17 00:00:00 2001 From: Tatyana Date: Mon, 9 Oct 2023 21:33:59 -0500 Subject: [PATCH 14/21] fix: try to import semver --- packages/runtime/src/templates/handlerUtils.ts | 3 ++- packages/runtime/src/templates/server.ts | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/runtime/src/templates/handlerUtils.ts b/packages/runtime/src/templates/handlerUtils.ts index f0d1d23c7e..5e618aa603 100644 --- a/packages/runtime/src/templates/handlerUtils.ts +++ b/packages/runtime/src/templates/handlerUtils.ts @@ -8,6 +8,7 @@ import { promisify } from 'util' import { HandlerEvent, HandlerResponse } from '@netlify/functions' import { http, https } from 'follow-redirects' import NextNodeServer from 'next/dist/server/next-server' +import { satisfies } from 'semver' import type { StaticRoute } from '../helpers/types' @@ -292,7 +293,7 @@ const nextPluginVersion = async () => { return packagePlugin?.version } -export const nextVersionNum = async (satisfies) => satisfies(await nextPluginVersion(), '13.3.3 - 13.4.9') +export const nextVersionNum = async (sem?) => (sem ?? satisfies)(await nextPluginVersion(), '13.3.3 - 13.4.9') export const getMatchedRoute = ( paths: string, diff --git a/packages/runtime/src/templates/server.ts b/packages/runtime/src/templates/server.ts index 59b5c26c28..bfeb884cbe 100644 --- a/packages/runtime/src/templates/server.ts +++ b/packages/runtime/src/templates/server.ts @@ -6,7 +6,6 @@ import { NextConfig } from 'next' import type { PrerenderManifest } from 'next/dist/build' import type { BaseNextResponse } from 'next/dist/server/base-http' import type { NodeRequestHandler, Options } from 'next/dist/server/next-server' -import { satisfies } from 'semver' import { ExperimentalConfigWithLegacy } from '../helpers/utils' @@ -68,7 +67,7 @@ const getNetlifyNextServer = (NextServer: NextServerType) => { // conditionally use the prebundled React module // PrebundledReact should only apply when appDir is set it falls between the specified Next versions const { experimental }: NextConfigWithAppDir = this.nextConfig - const version = await nextVersionNum(satisfies) + const version = await nextVersionNum() if (experimental?.appDir && version) this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl) // intercept on-demand revalidation requests and handle with the Netlify API From ba6688028e9f8a194ee2dd21b17bb5322d635666 Mon Sep 17 00:00:00 2001 From: Tatyana Date: Tue, 10 Oct 2023 01:08:25 -0500 Subject: [PATCH 15/21] fix: testing something new --- packages/runtime/src/helpers/functions.ts | 4 ++- .../runtime/src/helpers/functionsMetaData.ts | 7 +++-- packages/runtime/src/templates/getHandler.ts | 30 ++++++++++--------- .../runtime/src/templates/handlerUtils.ts | 23 +------------- packages/runtime/src/templates/server.ts | 4 +-- 5 files changed, 26 insertions(+), 42 deletions(-) diff --git a/packages/runtime/src/helpers/functions.ts b/packages/runtime/src/helpers/functions.ts index e14e3cf0c6..24e244f97b 100644 --- a/packages/runtime/src/helpers/functions.ts +++ b/packages/runtime/src/helpers/functions.ts @@ -28,7 +28,7 @@ import { getResolverForPages, getResolverForSourceFiles } from '../templates/get import { ApiConfig, extractConfigFromFile, isEdgeConfig } from './analysis' import { getRequiredServerFiles } from './config' import { getDependenciesOfFile, getServerFile, getSourceFileForPage } from './files' -import { writeFunctionConfiguration } from './functionsMetaData' +import { writeFunctionConfiguration, nextVersionNum } from './functionsMetaData' import { pack } from './pack' import { ApiRouteType } from './types' import { getFunctionNameForPage } from './utils' @@ -132,11 +132,13 @@ export const generateFunctions = async ( } const writeHandler = async (functionName: string, functionTitle: string, isODB: boolean) => { + const useHooks = await nextVersionNum() const handlerSource = getHandler({ isODB, publishDir, appDir: relative(functionDir, appDir), nextServerModuleRelativeLocation, + useHooks, }) await ensureDir(join(functionsDir, functionName)) diff --git a/packages/runtime/src/helpers/functionsMetaData.ts b/packages/runtime/src/helpers/functionsMetaData.ts index 897de45f9d..a2d55ea818 100644 --- a/packages/runtime/src/helpers/functionsMetaData.ts +++ b/packages/runtime/src/helpers/functionsMetaData.ts @@ -1,5 +1,6 @@ import { existsSync, readJSON, writeFile } from 'fs-extra' import { join } from 'pathe' +import { satisfies } from 'semver' import { NEXT_PLUGIN, NEXT_PLUGIN_NAME } from '../constants' @@ -17,8 +18,8 @@ const getNextRuntimeVersion = async (packageJsonPath: string, useNodeModulesPath const PLUGIN_PACKAGE_PATH = '.netlify/plugins/package.json' -const nextPluginVersion = async () => { - const moduleRoot = resolveModuleRoot(NEXT_PLUGIN) +const nextPluginVersion = async (module?: string) => { + const moduleRoot = resolveModuleRoot(module || NEXT_PLUGIN) const nodeModulesPath = moduleRoot ? join(moduleRoot, 'package.json') : null return ( @@ -31,6 +32,8 @@ const nextPluginVersion = async () => { export const getPluginVersion = async () => `${NEXT_PLUGIN_NAME}@${await nextPluginVersion()}` +export const nextVersionNum = async () => satisfies(await nextPluginVersion('next'), '13.3.3 - 13.4.9') + // The information needed to create a function configuration file export interface FunctionInfo { // The name of the function, e.g. `___netlify-handler` diff --git a/packages/runtime/src/templates/getHandler.ts b/packages/runtime/src/templates/getHandler.ts index 87389e714f..41437999ec 100644 --- a/packages/runtime/src/templates/getHandler.ts +++ b/packages/runtime/src/templates/getHandler.ts @@ -17,7 +17,6 @@ const path = require('path') const { URLSearchParams, URL } = require('url') const { Bridge } = require('@vercel/node-bridge/bridge') -const { satisfies } = require('semver') const { augmentFsModule, @@ -25,7 +24,6 @@ const { getMultiValueHeaders, getPrefetchResponse, normalizePath, - nextVersionNum, } = require('./handlerUtils') const { overrideRequireHooks, applyRequireHooks } = require('./requireHooks') const { getNetlifyNextServer } = require('./server') @@ -42,11 +40,20 @@ type MakeHandlerParams = { NextServer: NextServerType staticManifest: Array<[string, string]> mode: 'ssr' | 'odb' + useHooks: boolean } // We return a function and then call `toString()` on it to serialise it as the launcher function // eslint-disable-next-line max-lines-per-function -const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mode = 'ssr' }: MakeHandlerParams) => { +const makeHandler = ({ + conf, + app, + pageRoot, + NextServer, + staticManifest = [], + mode = 'ssr', + useHooks, +}: MakeHandlerParams) => { // Change working directory into the site root, unless using Nx, which moves the // dist directory and handles this itself const dir = path.resolve(__dirname, app) @@ -63,15 +70,10 @@ const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mod const { appDir }: ExperimentalConfigWithLegacy = conf.experimental // Next 13.4 conditionally uses different React versions and we need to make sure we use the same one // With the release of 13.5 experimental.appDir is no longer used. - // we will need to check if appDir is set and Next version before running requireHooks - const runRequireHooks = async (hook) => - await nextVersionNum(satisfies) - .then((version) => (appDir && version ? hook : null)) - .catch(() => ({})) - - runRequireHooks(overrideRequireHooks(conf.experimental)) + // we will need to check if appDir is set and Next version before running requireHook + if (appDir && useHooks) return overrideRequireHooks(conf.experimental) const NetlifyNextServer: NetlifyNextServerType = getNetlifyNextServer(NextServer) - runRequireHooks(applyRequireHooks()) + if (appDir && useHooks) return applyRequireHooks() const ONE_YEAR_IN_SECONDS = 31536000 @@ -216,6 +218,7 @@ export const getHandler = ({ publishDir = '../../../.next', appDir = '../../..', nextServerModuleRelativeLocation, + useHooks, }): string => // 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) javascript/* javascript */ ` @@ -227,7 +230,6 @@ export const getHandler = ({ const { Server } = require("http"); const { promises } = require("fs"); - const { satisfies } = require('semver') // We copy the file here rather than requiring from the node module const { Bridge } = require("./bridge"); const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, normalizePath, nextVersionNum } = require('./handlerUtils') @@ -244,7 +246,7 @@ export const getHandler = ({ const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", "server")); exports.handler = ${ isODB - ? `builder((${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'odb' }));` - : `(${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'ssr' });` + ? `builder((${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'odb', useHooks: ${useHooks} }));` + : `(${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'ssr', useHooks: ${useHooks} });` } ` diff --git a/packages/runtime/src/templates/handlerUtils.ts b/packages/runtime/src/templates/handlerUtils.ts index 5e618aa603..b905293220 100644 --- a/packages/runtime/src/templates/handlerUtils.ts +++ b/packages/runtime/src/templates/handlerUtils.ts @@ -1,14 +1,13 @@ import fs, { createWriteStream, existsSync } from 'fs' import { ServerResponse } from 'http' import { tmpdir } from 'os' -import path, { dirname, join, relative } from 'path' +import path from 'path' import { pipeline } from 'stream' import { promisify } from 'util' import { HandlerEvent, HandlerResponse } from '@netlify/functions' import { http, https } from 'follow-redirects' import NextNodeServer from 'next/dist/server/next-server' -import { satisfies } from 'semver' import type { StaticRoute } from '../helpers/types' @@ -275,26 +274,6 @@ export const localizeDataRoute = (dataRoute: string, localizedRoute: string): st .replace(/\/index\.json$/, '.json') } -export const resolveModuleRoot = (moduleName) => { - try { - return dirname(relative(process.cwd(), require.resolve(`${moduleName}/package.json`, { paths: [process.cwd()] }))) - } catch { - return null - } -} -// Had to copy nextPluginVersion logic from functionsMetaData for it to work within server.ts and getHandler.ts -const nextPluginVersion = async () => { - const moduleRoot = resolveModuleRoot('next') - if (!existsSync(moduleRoot)) { - return - } - const packagePath = join(moduleRoot, 'package.json') - const packagePlugin = await fs.promises.readFile(packagePath, 'utf-8').then((contents) => JSON.parse(contents)) - return packagePlugin?.version -} - -export const nextVersionNum = async (sem?) => (sem ?? satisfies)(await nextPluginVersion(), '13.3.3 - 13.4.9') - export const getMatchedRoute = ( paths: string, routesManifest: Array, diff --git a/packages/runtime/src/templates/server.ts b/packages/runtime/src/templates/server.ts index bfeb884cbe..07e4d7c416 100644 --- a/packages/runtime/src/templates/server.ts +++ b/packages/runtime/src/templates/server.ts @@ -17,7 +17,6 @@ import { localizeDataRoute, unlocalizeRoute, getMatchedRoute, - nextVersionNum, } from './handlerUtils' interface NetlifyConfig { @@ -67,8 +66,7 @@ const getNetlifyNextServer = (NextServer: NextServerType) => { // conditionally use the prebundled React module // PrebundledReact should only apply when appDir is set it falls between the specified Next versions const { experimental }: NextConfigWithAppDir = this.nextConfig - const version = await nextVersionNum() - if (experimental?.appDir && version) this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl) + if (experimental?.appDir) this.netlifyPrebundleReact(url, this.nextConfig, parsedUrl) // intercept on-demand revalidation requests and handle with the Netlify API if (headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) { From f4e75097295c1c6bef83e227ee0b767896ce5e68 Mon Sep 17 00:00:00 2001 From: Tatyana Date: Tue, 10 Oct 2023 09:31:19 -0500 Subject: [PATCH 16/21] fix: reset --- packages/runtime/src/helpers/functions.ts | 4 +-- packages/runtime/src/templates/getHandler.ts | 27 ++++++++------------ 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/packages/runtime/src/helpers/functions.ts b/packages/runtime/src/helpers/functions.ts index 24e244f97b..e14e3cf0c6 100644 --- a/packages/runtime/src/helpers/functions.ts +++ b/packages/runtime/src/helpers/functions.ts @@ -28,7 +28,7 @@ import { getResolverForPages, getResolverForSourceFiles } from '../templates/get import { ApiConfig, extractConfigFromFile, isEdgeConfig } from './analysis' import { getRequiredServerFiles } from './config' import { getDependenciesOfFile, getServerFile, getSourceFileForPage } from './files' -import { writeFunctionConfiguration, nextVersionNum } from './functionsMetaData' +import { writeFunctionConfiguration } from './functionsMetaData' import { pack } from './pack' import { ApiRouteType } from './types' import { getFunctionNameForPage } from './utils' @@ -132,13 +132,11 @@ export const generateFunctions = async ( } const writeHandler = async (functionName: string, functionTitle: string, isODB: boolean) => { - const useHooks = await nextVersionNum() const handlerSource = getHandler({ isODB, publishDir, appDir: relative(functionDir, appDir), nextServerModuleRelativeLocation, - useHooks, }) await ensureDir(join(functionsDir, functionName)) diff --git a/packages/runtime/src/templates/getHandler.ts b/packages/runtime/src/templates/getHandler.ts index 41437999ec..f801906cce 100644 --- a/packages/runtime/src/templates/getHandler.ts +++ b/packages/runtime/src/templates/getHandler.ts @@ -40,20 +40,11 @@ type MakeHandlerParams = { NextServer: NextServerType staticManifest: Array<[string, string]> mode: 'ssr' | 'odb' - useHooks: boolean } // We return a function and then call `toString()` on it to serialise it as the launcher function // eslint-disable-next-line max-lines-per-function -const makeHandler = ({ - conf, - app, - pageRoot, - NextServer, - staticManifest = [], - mode = 'ssr', - useHooks, -}: MakeHandlerParams) => { +const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mode = 'ssr' }: MakeHandlerParams) => { // Change working directory into the site root, unless using Nx, which moves the // dist directory and handles this itself const dir = path.resolve(__dirname, app) @@ -70,10 +61,15 @@ const makeHandler = ({ const { appDir }: ExperimentalConfigWithLegacy = conf.experimental // Next 13.4 conditionally uses different React versions and we need to make sure we use the same one // With the release of 13.5 experimental.appDir is no longer used. - // we will need to check if appDir is set and Next version before running requireHook - if (appDir && useHooks) return overrideRequireHooks(conf.experimental) + // we will need to check if appDir is set and Next version before running requireHooks + // const runRequireHooks = async (hook) => + // await nextVersionNum() + // .then((version) => (appDir && version ? hook : null)) + // .catch(() => ({})) + + if (appDir) overrideRequireHooks(conf.experimental) const NetlifyNextServer: NetlifyNextServerType = getNetlifyNextServer(NextServer) - if (appDir && useHooks) return applyRequireHooks() + if (appDir) applyRequireHooks() const ONE_YEAR_IN_SECONDS = 31536000 @@ -218,7 +214,6 @@ export const getHandler = ({ publishDir = '../../../.next', appDir = '../../..', nextServerModuleRelativeLocation, - useHooks, }): string => // 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) javascript/* javascript */ ` @@ -246,7 +241,7 @@ export const getHandler = ({ const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", "server")); exports.handler = ${ isODB - ? `builder((${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'odb', useHooks: ${useHooks} }));` - : `(${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'ssr', useHooks: ${useHooks} });` + ? `builder((${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'odb'}));` + : `(${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'ssr'});` } ` From a8d4a325755a140430899a9fbbd879f1c5046d7c Mon Sep 17 00:00:00 2001 From: Tatyana Date: Tue, 10 Oct 2023 10:10:02 -0500 Subject: [PATCH 17/21] fix: add back useHooks --- packages/runtime/src/helpers/functions.ts | 4 +++- packages/runtime/src/templates/getHandler.ts | 25 ++++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/runtime/src/helpers/functions.ts b/packages/runtime/src/helpers/functions.ts index e14e3cf0c6..24e244f97b 100644 --- a/packages/runtime/src/helpers/functions.ts +++ b/packages/runtime/src/helpers/functions.ts @@ -28,7 +28,7 @@ import { getResolverForPages, getResolverForSourceFiles } from '../templates/get import { ApiConfig, extractConfigFromFile, isEdgeConfig } from './analysis' import { getRequiredServerFiles } from './config' import { getDependenciesOfFile, getServerFile, getSourceFileForPage } from './files' -import { writeFunctionConfiguration } from './functionsMetaData' +import { writeFunctionConfiguration, nextVersionNum } from './functionsMetaData' import { pack } from './pack' import { ApiRouteType } from './types' import { getFunctionNameForPage } from './utils' @@ -132,11 +132,13 @@ export const generateFunctions = async ( } const writeHandler = async (functionName: string, functionTitle: string, isODB: boolean) => { + const useHooks = await nextVersionNum() const handlerSource = getHandler({ isODB, publishDir, appDir: relative(functionDir, appDir), nextServerModuleRelativeLocation, + useHooks, }) await ensureDir(join(functionsDir, functionName)) diff --git a/packages/runtime/src/templates/getHandler.ts b/packages/runtime/src/templates/getHandler.ts index f801906cce..f04be6bae5 100644 --- a/packages/runtime/src/templates/getHandler.ts +++ b/packages/runtime/src/templates/getHandler.ts @@ -40,11 +40,20 @@ type MakeHandlerParams = { NextServer: NextServerType staticManifest: Array<[string, string]> mode: 'ssr' | 'odb' + useHooks: boolean } // We return a function and then call `toString()` on it to serialise it as the launcher function // eslint-disable-next-line max-lines-per-function -const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mode = 'ssr' }: MakeHandlerParams) => { +const makeHandler = ({ + conf, + app, + pageRoot, + NextServer, + staticManifest = [], + mode = 'ssr', + useHooks, +}: MakeHandlerParams) => { // Change working directory into the site root, unless using Nx, which moves the // dist directory and handles this itself const dir = path.resolve(__dirname, app) @@ -62,14 +71,9 @@ const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mod // Next 13.4 conditionally uses different React versions and we need to make sure we use the same one // With the release of 13.5 experimental.appDir is no longer used. // we will need to check if appDir is set and Next version before running requireHooks - // const runRequireHooks = async (hook) => - // await nextVersionNum() - // .then((version) => (appDir && version ? hook : null)) - // .catch(() => ({})) - - if (appDir) overrideRequireHooks(conf.experimental) + if (appDir && useHooks) overrideRequireHooks(conf.experimental) const NetlifyNextServer: NetlifyNextServerType = getNetlifyNextServer(NextServer) - if (appDir) applyRequireHooks() + if (appDir && useHooks) applyRequireHooks() const ONE_YEAR_IN_SECONDS = 31536000 @@ -214,6 +218,7 @@ export const getHandler = ({ publishDir = '../../../.next', appDir = '../../..', nextServerModuleRelativeLocation, + useHooks, }): string => // 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) javascript/* javascript */ ` @@ -241,7 +246,7 @@ export const getHandler = ({ const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", "server")); exports.handler = ${ isODB - ? `builder((${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'odb'}));` - : `(${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'ssr'});` + ? `builder((${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'odb', useHooks: ${useHooks}}));` + : `(${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'ssr', useHooks: ${useHooks}});` } ` From ee2b881b2e0b0b82c926ecf8d32f29dfb1af0596 Mon Sep 17 00:00:00 2001 From: Tatyana Date: Tue, 10 Oct 2023 10:51:25 -0500 Subject: [PATCH 18/21] fix: test updates + remove preRender update --- packages/runtime/src/helpers/edge.ts | 1 - packages/runtime/src/helpers/functions.ts | 4 ++-- packages/runtime/src/helpers/functionsMetaData.ts | 2 +- packages/runtime/src/templates/server.ts | 10 ++-------- test/templates/server.spec.ts | 2 +- 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/packages/runtime/src/helpers/edge.ts b/packages/runtime/src/helpers/edge.ts index 895c501922..4187d6cded 100644 --- a/packages/runtime/src/helpers/edge.ts +++ b/packages/runtime/src/helpers/edge.ts @@ -462,7 +462,6 @@ export const writeEdgeFunctions = async ({ function: functionName, name: edgeFunctionDefinition.name, pattern, - // cache: "manual" is currently experimental, so we restrict it to sites that use experimental appDir cache: 'manual', generator, }) diff --git a/packages/runtime/src/helpers/functions.ts b/packages/runtime/src/helpers/functions.ts index 24e244f97b..f7ded83ed1 100644 --- a/packages/runtime/src/helpers/functions.ts +++ b/packages/runtime/src/helpers/functions.ts @@ -28,7 +28,7 @@ import { getResolverForPages, getResolverForSourceFiles } from '../templates/get import { ApiConfig, extractConfigFromFile, isEdgeConfig } from './analysis' import { getRequiredServerFiles } from './config' import { getDependenciesOfFile, getServerFile, getSourceFileForPage } from './files' -import { writeFunctionConfiguration, nextVersionNum } from './functionsMetaData' +import { writeFunctionConfiguration, useRequireHooks } from './functionsMetaData' import { pack } from './pack' import { ApiRouteType } from './types' import { getFunctionNameForPage } from './utils' @@ -132,7 +132,7 @@ export const generateFunctions = async ( } const writeHandler = async (functionName: string, functionTitle: string, isODB: boolean) => { - const useHooks = await nextVersionNum() + const useHooks = await useRequireHooks() const handlerSource = getHandler({ isODB, publishDir, diff --git a/packages/runtime/src/helpers/functionsMetaData.ts b/packages/runtime/src/helpers/functionsMetaData.ts index a2d55ea818..477a88f0b7 100644 --- a/packages/runtime/src/helpers/functionsMetaData.ts +++ b/packages/runtime/src/helpers/functionsMetaData.ts @@ -32,7 +32,7 @@ const nextPluginVersion = async (module?: string) => { export const getPluginVersion = async () => `${NEXT_PLUGIN_NAME}@${await nextPluginVersion()}` -export const nextVersionNum = async () => satisfies(await nextPluginVersion('next'), '13.3.3 - 13.4.9') +export const useRequireHooks = async () => satisfies(await nextPluginVersion('next'), '13.3.3 - 13.4.9') // The information needed to create a function configuration file export interface FunctionInfo { diff --git a/packages/runtime/src/templates/server.ts b/packages/runtime/src/templates/server.ts index 07e4d7c416..dc137afcc1 100644 --- a/packages/runtime/src/templates/server.ts +++ b/packages/runtime/src/templates/server.ts @@ -1,4 +1,3 @@ -import { join } from 'path' // eslint-disable-next-line n/no-deprecated-api -- this is what Next.js uses as well import { parse } from 'url' @@ -36,16 +35,11 @@ const getNetlifyNextServer = (NextServer: NextServerType) => { return this.nextConfig.experimental?.serverActions ? 'experimental' : 'next' } - protected getManifest(manifest: string) { - // eslint-disable-next-line import/no-dynamic-require - return require(join(this.distDir, manifest)) - } - public constructor(options: Options, netlifyConfig: NetlifyConfig) { super(options) this.netlifyConfig = netlifyConfig // copy the prerender manifest so it doesn't get mutated by Next.js - const manifest = this.getPrerenderManifest() || this.getManifest('prerender-manifest.json') + const manifest = this.getPrerenderManifest() this.netlifyPrerenderManifest = { ...manifest, routes: { ...manifest.routes }, @@ -96,7 +90,7 @@ const getNetlifyNextServer = (NextServer: NextServerType) => { // doing what they do in https://github.com/vercel/vercel/blob/1663db7ca34d3dd99b57994f801fb30b72fbd2f3/packages/next/src/server-build.ts#L576-L580 private async netlifyPrebundleReact(path: string, { basePath, trailingSlash }: NextConfig, parsedUrl) { - const routesManifest = this.getRoutesManifest?.() || this.getManifest('routes-manifest.json') + const routesManifest = this.getRoutesManifest?.() const appPathsRoutes = this.getAppPathRoutes?.() const routes = routesManifest && [...routesManifest.staticRoutes, ...routesManifest.dynamicRoutes] const matchedRoute = await getMatchedRoute(path, routes, parsedUrl, basePath, trailingSlash) diff --git a/test/templates/server.spec.ts b/test/templates/server.spec.ts index a83b18f63c..5610e3d3b5 100644 --- a/test/templates/server.spec.ts +++ b/test/templates/server.spec.ts @@ -296,7 +296,7 @@ describe('the netlify next server', () => { await requestHandler(new NodeNextRequest(mockReq), new NodeNextResponse(mockRes)) // eslint-disable-next-line no-underscore-dangle - expect(process.env.__NEXT_PRIVATE_PREBUNDLED_REACT).toBe('') + expect(process.env.__NEXT_PRIVATE_PREBUNDLED_REACT).toBeFalsy() }) it('resolves the prebundled react version for app routes', async () => { From 847b506e71535f3bcdbcf5a167889533b6e24c99 Mon Sep 17 00:00:00 2001 From: Tatyana Date: Tue, 10 Oct 2023 11:32:18 -0500 Subject: [PATCH 19/21] test: add useHooks --- test/index.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/index.spec.ts b/test/index.spec.ts index def83d27d1..ae12982e9c 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -559,10 +559,10 @@ describe('onBuild()', () => { expect(existsSync(odbHandlerFile)).toBeTruthy() expect(readFileSync(handlerFile, 'utf8')).toMatch( - `({ conf: config, app: "../../..", pageRoot, NextServer, staticManifest, mode: 'ssr' })`, + `({ conf: config, app: "../../..", pageRoot, NextServer, staticManifest, mode: 'ssr', useHooks: false})`, ) expect(readFileSync(odbHandlerFile, 'utf8')).toMatch( - `({ conf: config, app: "../../..", pageRoot, NextServer, staticManifest, mode: 'odb' })`, + `({ conf: config, app: "../../..", pageRoot, NextServer, staticManifest, mode: 'odb', useHooks: false})`, ) expect(readFileSync(handlerFile, 'utf8')).toMatch(`require("../../../.next/required-server-files.json")`) expect(readFileSync(odbHandlerFile, 'utf8')).toMatch(`require("../../../.next/required-server-files.json")`) From f9f42d727de5ce57889d4784f267f47f24b20c3a Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 10 Oct 2023 18:42:54 +0200 Subject: [PATCH 20/21] refactor: use existing resolveModuleRoot function instead of module.createRequire --- packages/runtime/src/helpers/config.ts | 20 ++++++++------------ test/index.spec.ts | 2 +- test/test-utils.ts | 7 +++++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/runtime/src/helpers/config.ts b/packages/runtime/src/helpers/config.ts index 10717ca3a8..dc34fb3ed8 100644 --- a/packages/runtime/src/helpers/config.ts +++ b/packages/runtime/src/helpers/config.ts @@ -1,5 +1,3 @@ -import mod from 'module' - import type { NetlifyConfig } from '@netlify/build/types' import destr from 'destr' import { readJSON, writeJSON } from 'fs-extra' @@ -75,9 +73,9 @@ export const updateRequiredServerFiles = async (publish: string, modifiedConfig: await writeJSON(configFile, modifiedConfig) } -export const resolveModuleRoot = (moduleName) => { +export const resolveModuleRoot = (moduleName, paths = [process.cwd()]) => { try { - return dirname(relative(process.cwd(), require.resolve(`${moduleName}/package.json`, { paths: [process.cwd()] }))) + return dirname(relative(process.cwd(), require.resolve(`${moduleName}/package.json`, { paths }))) } catch { return null } @@ -164,18 +162,16 @@ export const configureHandlerFunctions = async ({ `!${nextRoot}/dist/compiled/webpack/bundle4.js`, `!${nextRoot}/dist/compiled/webpack/bundle5.js`, ) - } - try { // on Next 13.5+ there is no longer statically analyzable import to styled-jsx/style // so lambda fails to bundle it. Next require hooks actually try to resolve it // and fail if it is not bundled, so we forcefully add it to lambda. - - // eslint-disable-next-line n/no-unsupported-features/node-builtins - const nextRequire = mod.createRequire(require.resolve(`next`)) - const styledJsxPath = nextRequire.resolve(`styled-jsx/style`) - netlifyConfig.functions[functionName].included_files.push(styledJsxPath) - } catch {} + const styledJsxRoot = resolveModuleRoot('styled-jsx', [join(process.cwd(), nextRoot)]) + if (styledJsxRoot) { + const styledJsxStyleModulePath = join(styledJsxRoot, 'style.js') + netlifyConfig.functions[functionName].included_files.push(styledJsxStyleModulePath) + } + } excludedModules.forEach((moduleName) => { const moduleRoot = resolveModuleRoot(moduleName) diff --git a/test/index.spec.ts b/test/index.spec.ts index ae12982e9c..d095709c76 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -455,7 +455,7 @@ describe('onBuild()', () => { `!node_modules/next/dist/next-server/server/lib/squoosh/**/*.wasm`, '!node_modules/next/dist/compiled/webpack/bundle4.js', '!node_modules/next/dist/compiled/webpack/bundle5.js', - '/home/runner/work/next-runtime/next-runtime/node_modules/styled-jsx/style.js', + 'node_modules/styled-jsx/style.js', '!node_modules/sharp/**/*', ] // Relative paths in Windows are different diff --git a/test/test-utils.ts b/test/test-utils.ts index 76313c1a69..14e7d9d7f1 100644 --- a/test/test-utils.ts +++ b/test/test-utils.ts @@ -1,7 +1,7 @@ import path, { dirname } from 'path' import cpy from 'cpy' -import { writeJSON, existsSync, ensureDir, readJson, copy } from 'fs-extra' +import { writeJSON, writeFile, existsSync, ensureDir, readJson, copy } from 'fs-extra' import { dir as getTmpDir } from 'tmp-promise' const FIXTURES_DIR = `${__dirname}/fixtures` @@ -26,7 +26,7 @@ const rewriteAppDir = async function (dir = '.next') { // Move .next from sample project to current directory export const moveNextDist = async function (dir = '.next', copyMods = false) { - await (copyMods ? copyModules(['next', 'sharp']) : stubModules(['next', 'sharp'])) + await (copyMods ? copyModules(['next', 'sharp', 'styled-jsx']) : stubModules(['next', 'sharp', 'styled-jsx'])) await ensureDir(dirname(dir)) await copy(path.join(SAMPLE_PROJECT_DIR, '.next'), path.join(process.cwd(), dir)) @@ -53,6 +53,9 @@ export const stubModules = async function (modules) { const dir = path.join(process.cwd(), 'node_modules', mod) await ensureDir(dir) await writeJSON(path.join(dir, 'package.json'), { name: mod }) + if (mod === `styled-jsx`) { + await writeFile('style.js', '') + } } } From d5e6ea04d6893581d24f0279730be5606332758e Mon Sep 17 00:00:00 2001 From: Tatyana Date: Tue, 10 Oct 2023 11:51:35 -0500 Subject: [PATCH 21/21] fix: remove 13.5 --- demos/next-i18next/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/next-i18next/package.json b/demos/next-i18next/package.json index a94f8097dd..713b65adf7 100644 --- a/demos/next-i18next/package.json +++ b/demos/next-i18next/package.json @@ -9,7 +9,7 @@ "lint": "next lint" }, "dependencies": { - "next": "^13.5.4", + "next": "^13.4.1", "next-i18next": "^11.0.0", "react": "^18.2.0", "react-dom": "^18.2.0"