From fb2324455c2faaa2f6dddf0f1aa6563a614810cd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 04:51:08 +0000 Subject: [PATCH 1/9] chore(deps): update dependency @types/node to v20.16.2 (#2569) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index a2d0b92394..b26cb35d69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7022,12 +7022,12 @@ } }, "node_modules/@types/node": { - "version": "20.14.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.14.tgz", - "integrity": "sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==", + "version": "20.16.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.2.tgz", + "integrity": "sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==", "dev": true, "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "node_modules/@types/normalize-package-data": { @@ -18706,9 +18706,9 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -24845,12 +24845,12 @@ } }, "@types/node": { - "version": "20.14.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.14.tgz", - "integrity": "sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==", + "version": "20.16.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.2.tgz", + "integrity": "sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==", "dev": true, "requires": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "@types/normalize-package-data": { @@ -33217,9 +33217,9 @@ } }, "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true }, "unicode-canonical-property-names-ecmascript": { From 4a5351263591d112da1b985e9919c841b6059b7d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:43:35 +0000 Subject: [PATCH 2/9] fix(deps): update dependency @netlify/plugin-nextjs to ^5.7.0 (#2576) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- e2e-report/package-lock.json | 8 ++++---- e2e-report/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/e2e-report/package-lock.json b/e2e-report/package-lock.json index 8373c75a4b..a10be5749a 100644 --- a/e2e-report/package-lock.json +++ b/e2e-report/package-lock.json @@ -8,7 +8,7 @@ "name": "e2e-test-site", "version": "0.2.0", "dependencies": { - "@netlify/plugin-nextjs": "^5.6.0", + "@netlify/plugin-nextjs": "^5.7.0", "next": "^14.2.3", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -260,9 +260,9 @@ } }, "node_modules/@netlify/plugin-nextjs": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@netlify/plugin-nextjs/-/plugin-nextjs-5.6.0.tgz", - "integrity": "sha512-PBrsd/GJZ9MN8BdyIoleTkY22lAUMfcRxrbb8wgxGzXtTW0RU0GW2mc99ISB6zOwWMZ11rSjeN0GS6znnukvww==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@netlify/plugin-nextjs/-/plugin-nextjs-5.7.0.tgz", + "integrity": "sha512-HFZBHz70oDmw/5tKXH3F6WX4X01/DgY9etjLXjfDSxYl+AiNKwL+1kePUMp73zMFN5M+TU/TsEaFr38wmE6EVQ==", "license": "MIT", "engines": { "node": ">=18.0.0" diff --git a/e2e-report/package.json b/e2e-report/package.json index 7b03981ee9..a4f8e8c906 100644 --- a/e2e-report/package.json +++ b/e2e-report/package.json @@ -9,7 +9,7 @@ "lint": "next lint" }, "dependencies": { - "@netlify/plugin-nextjs": "^5.6.0", + "@netlify/plugin-nextjs": "^5.7.0", "next": "^14.2.3", "react": "^18.3.1", "react-dom": "^18.3.1" From 83f685e15552cee1c8a4d6c68251c01ff1cfe3fb Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Thu, 29 Aug 2024 20:38:43 +0200 Subject: [PATCH 3/9] fix: update cache handler to accommodate changes in next@canary (#2572) * update react version needed by next@canary * tmp: just checking if canary tests will be happier * what's up with NODE_ENV? * missing PAGES * chore: use npm info to figure out react version needed for canary instead of hardcoding it * chore: drop package lock file when preparing fixtures * test: unset more things related to next's fetch patching * use correct cache kind for initial cache seeding depending on next version * small cleanup * typehell * just checking * proper NODE_ENV setting with explanation * any is bad * Update src/shared/cache-types.cts Co-authored-by: Philippe Serhal * add note about deleting next-patch symbol --------- Co-authored-by: Philippe Serhal --- src/build/content/prerendered.ts | 31 +++++-- src/run/handlers/cache.cts | 67 +++++++++------ src/run/next.cts | 9 ++ src/shared/cache-types.cts | 122 +++++++++++++++++++++------ tests/prepare.mjs | 7 +- tests/utils/fixture.ts | 4 + tests/utils/next-version-helpers.mjs | 14 ++- 7 files changed, 194 insertions(+), 60 deletions(-) diff --git a/src/build/content/prerendered.ts b/src/build/content/prerendered.ts index 9f1ae2fe75..6c45a74472 100644 --- a/src/build/content/prerendered.ts +++ b/src/build/content/prerendered.ts @@ -10,7 +10,7 @@ import { satisfies } from 'semver' import { encodeBlobKey } from '../../shared/blobkey.js' import type { - CachedFetchValue, + CachedFetchValueForMultipleVersions, NetlifyCachedAppPageValue, NetlifyCachedPageValue, NetlifyCachedRouteValue, @@ -45,8 +45,11 @@ const writeCacheEntry = async ( */ const routeToFilePath = (path: string) => (path === '/' ? '/index' : path) -const buildPagesCacheValue = async (path: string): Promise => ({ - kind: 'PAGE', +const buildPagesCacheValue = async ( + path: string, + shouldUseEnumKind: boolean, +): Promise => ({ + kind: shouldUseEnumKind ? 'PAGES' : 'PAGE', html: await readFile(`${path}.html`, 'utf-8'), pageData: JSON.parse(await readFile(`${path}.json`, 'utf-8')), headers: undefined, @@ -96,14 +99,17 @@ const buildAppCacheValue = async ( const buildRouteCacheValue = async ( path: string, initialRevalidateSeconds: number | false, + shouldUseEnumKind: boolean, ): Promise => ({ - kind: 'ROUTE', + kind: shouldUseEnumKind ? 'APP_ROUTE' : 'ROUTE', body: await readFile(`${path}.body`, 'base64'), ...JSON.parse(await readFile(`${path}.meta`, 'utf-8')), revalidate: initialRevalidateSeconds, }) -const buildFetchCacheValue = async (path: string): Promise => ({ +const buildFetchCacheValue = async ( + path: string, +): Promise => ({ kind: 'FETCH', ...JSON.parse(await readFile(path, 'utf-8')), }) @@ -133,6 +139,13 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise }) : false + // https://github.com/vercel/next.js/pull/68602 changed the cache kind for Pages router pages from `PAGE` to `PAGES` and from `ROUTE` to `APP_ROUTE`. + const shouldUseEnumKind = ctx.nextVersion + ? satisfies(ctx.nextVersion, '>=15.0.0-canary.114 <15.0.0-d || >15.0.0-rc.0', { + includePrerelease: true, + }) + : false + await Promise.all( Object.entries(manifest.routes).map( ([route, meta]): Promise => @@ -152,7 +165,10 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise // if pages router returns 'notFound: true', build won't produce html and json files return } - value = await buildPagesCacheValue(join(ctx.publishDir, 'server/pages', key)) + value = await buildPagesCacheValue( + join(ctx.publishDir, 'server/pages', key), + shouldUseEnumKind, + ) break case meta.dataRoute?.endsWith('.rsc'): value = await buildAppCacheValue( @@ -164,6 +180,7 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise value = await buildRouteCacheValue( join(ctx.publishDir, 'server/app', key), meta.initialRevalidateSeconds, + shouldUseEnumKind, ) break default: @@ -171,7 +188,7 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise } // Netlify Forms are not support and require a workaround - if (value.kind === 'PAGE' || value.kind === 'APP_PAGE') { + if (value.kind === 'PAGE' || value.kind === 'PAGES' || value.kind === 'APP_PAGE') { verifyNetlifyForms(ctx, value.html) } diff --git a/src/run/handlers/cache.cts b/src/run/handlers/cache.cts index 790c89004e..32e569c065 100644 --- a/src/run/handlers/cache.cts +++ b/src/run/handlers/cache.cts @@ -11,14 +11,15 @@ import { type Span } from '@opentelemetry/api' import type { PrerenderManifest } from 'next/dist/build/index.js' import { NEXT_CACHE_TAGS_HEADER } from 'next/dist/lib/constants.js' -import type { - CacheHandler, - CacheHandlerContext, - IncrementalCache, - NetlifyCachedPageValue, - NetlifyCachedRouteValue, - NetlifyCacheHandlerValue, - NetlifyIncrementalCacheValue, +import { + type CacheHandlerContext, + type CacheHandlerForMultipleVersions, + isCachedPageValue, + isCachedRouteValue, + type NetlifyCachedPageValue, + type NetlifyCachedRouteValue, + type NetlifyCacheHandlerValue, + type NetlifyIncrementalCacheValue, } from '../../shared/cache-types.cjs' import { getRegionalBlobStore } from '../regional-blob-store.cjs' @@ -29,7 +30,7 @@ type TagManifest = { revalidatedAt: number } type TagManifestBlobCache = Record> -export class NetlifyCacheHandler implements CacheHandler { +export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { options: CacheHandlerContext revalidatedTags: string[] blobStore: Store @@ -132,13 +133,18 @@ export class NetlifyCacheHandler implements CacheHandler { if ( cacheValue.kind === 'PAGE' || + cacheValue.kind === 'PAGES' || cacheValue.kind === 'APP_PAGE' || - cacheValue.kind === 'ROUTE' + cacheValue.kind === 'ROUTE' || + cacheValue.kind === 'APP_ROUTE' ) { if (cacheValue.headers?.[NEXT_CACHE_TAGS_HEADER]) { const cacheTags = (cacheValue.headers[NEXT_CACHE_TAGS_HEADER] as string).split(',') requestContext.responseCacheTags = cacheTags - } else if (cacheValue.kind === 'PAGE' && typeof cacheValue.pageData === 'object') { + } else if ( + (cacheValue.kind === 'PAGE' || cacheValue.kind === 'PAGES') && + typeof cacheValue.pageData === 'object' + ) { // pages router doesn't have cache tags headers in PAGE cache value // so we need to generate appropriate cache tags for it const cacheTags = [`_N_T_${key === '/index' ? '/' : key}`] @@ -185,7 +191,9 @@ export class NetlifyCacheHandler implements CacheHandler { } } - async get(...args: Parameters): ReturnType { + async get( + ...args: Parameters + ): ReturnType { return this.tracer.withActiveSpan('get cache key', async (span) => { const [key, ctx = {}] = args getLogger().debug(`[NetlifyCacheHandler.get]: ${key}`) @@ -224,8 +232,12 @@ export class NetlifyCacheHandler implements CacheHandler { value: blob.value, } - case 'ROUTE': { - span.addEvent('ROUTE', { lastModified: blob.lastModified, status: blob.value.status }) + case 'ROUTE': + case 'APP_ROUTE': { + span.addEvent(blob.value?.kind, { + lastModified: blob.lastModified, + status: blob.value.status, + }) const valueWithoutRevalidate = this.captureRouteRevalidateAndRemoveFromObject(blob.value) @@ -237,8 +249,9 @@ export class NetlifyCacheHandler implements CacheHandler { }, } } - case 'PAGE': { - span.addEvent('PAGE', { lastModified: blob.lastModified }) + case 'PAGE': + case 'PAGES': { + span.addEvent(blob.value?.kind, { lastModified: blob.lastModified }) const { revalidate, ...restOfPageValue } = blob.value @@ -250,7 +263,7 @@ export class NetlifyCacheHandler implements CacheHandler { } } case 'APP_PAGE': { - span.addEvent('APP_PAGE', { lastModified: blob.lastModified }) + span.addEvent(blob.value?.kind, { lastModified: blob.lastModified }) const { revalidate, rscData, ...restOfPageValue } = blob.value @@ -272,10 +285,14 @@ export class NetlifyCacheHandler implements CacheHandler { } private transformToStorableObject( - data: Parameters[1], - context: Parameters[2], + data: Parameters[1], + context: Parameters[2], ): NetlifyIncrementalCacheValue | null { - if (data?.kind === 'ROUTE') { + if (!data) { + return null + } + + if (isCachedRouteValue(data)) { return { ...data, revalidate: context.revalidate, @@ -283,7 +300,7 @@ export class NetlifyCacheHandler implements CacheHandler { } } - if (data?.kind === 'PAGE') { + if (isCachedPageValue(data)) { return { ...data, revalidate: context.revalidate, @@ -301,7 +318,7 @@ export class NetlifyCacheHandler implements CacheHandler { return data } - async set(...args: Parameters) { + async set(...args: Parameters) { return this.tracer.withActiveSpan('set cache key', async (span) => { const [key, data, context] = args const blobKey = await this.encodeBlobKey(key) @@ -321,7 +338,7 @@ export class NetlifyCacheHandler implements CacheHandler { value, }) - if (data?.kind === 'PAGE') { + if (data?.kind === 'PAGE' || data?.kind === 'PAGES') { const requestContext = getRequestContext() if (requestContext?.didPagesRouterOnDemandRevalidate) { const tag = `_N_T_${key === '/index' ? '/' : key}` @@ -397,8 +414,10 @@ export class NetlifyCacheHandler implements CacheHandler { cacheTags = [...tags, ...softTags] } else if ( cacheEntry.value?.kind === 'PAGE' || + cacheEntry.value?.kind === 'PAGES' || cacheEntry.value?.kind === 'APP_PAGE' || - cacheEntry.value?.kind === 'ROUTE' + cacheEntry.value?.kind === 'ROUTE' || + cacheEntry.value?.kind === 'APP_ROUTE' ) { cacheTags = (cacheEntry.value.headers?.[NEXT_CACHE_TAGS_HEADER] as string)?.split(',') || [] } else { diff --git a/src/run/next.cts b/src/run/next.cts index edb6fd2a44..b99515e393 100644 --- a/src/run/next.cts +++ b/src/run/next.cts @@ -8,6 +8,15 @@ import { getRequestContext } from './handlers/request-context.cjs' import { getTracer } from './handlers/tracer.cjs' import { getRegionalBlobStore } from './regional-blob-store.cjs' +// https://github.com/vercel/next.js/pull/68193/files#diff-37243d614f1f5d3f7ea50bbf2af263f6b1a9a4f70e84427977781e07b02f57f1R49 +// This import resulted in importing unbundled React which depending if NODE_ENV is `production` or not would use +// either development or production version of React. When not set to `production` it would use development version +// which later cause mismatching problems when both development and production versions of React were loaded causing +// react errors. +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore ignoring readonly NODE_ENV +process.env.NODE_ENV = 'production' + console.time('import next server') // eslint-disable-next-line @typescript-eslint/no-var-requires diff --git a/src/shared/cache-types.cts b/src/shared/cache-types.cts index 06cee07516..bdc48bb973 100644 --- a/src/shared/cache-types.cts +++ b/src/shared/cache-types.cts @@ -1,53 +1,78 @@ import type { + CacheHandler, CacheHandlerValue, - IncrementalCache, } from 'next/dist/server/lib/incremental-cache/index.js' import type { + CachedFetchValue, CachedRouteValue, IncrementalCachedAppPageValue, + IncrementalCachedPageValue, IncrementalCacheValue, } from 'next/dist/server/response-cache/types.js' -export type { - CacheHandler, - CacheHandlerContext, - IncrementalCache, -} from 'next/dist/server/lib/incremental-cache/index.js' +export type { CacheHandlerContext } from 'next/dist/server/lib/incremental-cache/index.js' -export type NetlifyCachedRouteValue = Omit & { +/** + * Shape of the cache value that is returned from CacheHandler.get or passed to CacheHandler.set + */ +type CachedRouteValueForMultipleVersions = Omit & { + kind: 'ROUTE' | 'APP_ROUTE' +} + +/** + * Used for storing in blobs and reading from blobs + */ +export type NetlifyCachedRouteValue = Omit & { // Next.js stores body as buffer, while we store it as base64 encoded string body: string // Next.js doesn't produce cache-control tag we use to generate cdn cache control // so store needed values as part of cached response data - revalidate: Parameters[2]['revalidate'] + revalidate?: Parameters[2]['revalidate'] } -export type NetlifyCachedAppPageValue = Omit & { +/** + * Shape of the cache value that is returned from CacheHandler.get or passed to CacheHandler.set + */ +type IncrementalCachedAppPageValueForMultipleVersions = Omit< + IncrementalCachedAppPageValue, + 'kind' +> & { + kind: 'APP_PAGE' +} + +/** + * Used for storing in blobs and reading from blobs + */ +export type NetlifyCachedAppPageValue = Omit< + IncrementalCachedAppPageValueForMultipleVersions, + 'rscData' +> & { // Next.js stores rscData as buffer, while we store it as base64 encoded string rscData: string | undefined - revalidate?: Parameters[2]['revalidate'] + revalidate?: Parameters[2]['revalidate'] } -type CachedPageValue = Extract - -export type NetlifyCachedPageValue = CachedPageValue & { - revalidate?: Parameters[2]['revalidate'] +/** + * Shape of the cache value that is returned from CacheHandler.get or passed to CacheHandler.set + */ +type IncrementalCachedPageValueForMultipleVersions = Omit & { + kind: 'PAGE' | 'PAGES' } -export type CachedFetchValue = Extract +/** + * Used for storing in blobs and reading from blobs + */ +export type NetlifyCachedPageValue = IncrementalCachedPageValueForMultipleVersions & { + revalidate?: Parameters[2]['revalidate'] +} -export type NetlifyIncrementalCacheValue = - | Exclude< - IncrementalCacheValue, - CachedRouteValue | CachedPageValue | IncrementalCachedAppPageValue - > - | NetlifyCachedRouteValue - | NetlifyCachedPageValue - | NetlifyCachedAppPageValue +export type CachedFetchValueForMultipleVersions = Omit & { + kind: 'FETCH' +} type CachedRouteValueToNetlify = T extends CachedRouteValue ? NetlifyCachedRouteValue - : T extends CachedPageValue + : T extends IncrementalCachedPageValue ? NetlifyCachedPageValue : T extends IncrementalCachedAppPageValue ? NetlifyCachedAppPageValue @@ -55,4 +80,53 @@ type CachedRouteValueToNetlify = T extends CachedRouteValue type MapCachedRouteValueToNetlify = { [K in keyof T]: CachedRouteValueToNetlify } +/** + * Used for storing in blobs and reading from blobs + */ export type NetlifyCacheHandlerValue = MapCachedRouteValueToNetlify + +/** + * Used for storing in blobs and reading from blobs + */ +export type NetlifyIncrementalCacheValue = NetlifyCacheHandlerValue['value'] + +type IncrementalCacheValueToMultipleVersions = T extends CachedRouteValue + ? CachedRouteValueForMultipleVersions + : T extends IncrementalCachedPageValue + ? IncrementalCachedPageValueForMultipleVersions + : T extends IncrementalCachedAppPageValue + ? IncrementalCachedAppPageValueForMultipleVersions + : T extends CachedFetchValue + ? CachedFetchValueForMultipleVersions + : T extends CacheHandlerValue + ? { + [K in keyof T]: IncrementalCacheValueToMultipleVersions + } + : T + +type IncrementalCacheValueForMultipleVersions = + IncrementalCacheValueToMultipleVersions + +export const isCachedPageValue = ( + value: IncrementalCacheValueForMultipleVersions, +): value is IncrementalCachedPageValueForMultipleVersions => + value.kind === 'PAGE' || value.kind === 'PAGES' + +export const isCachedRouteValue = ( + value: IncrementalCacheValueForMultipleVersions, +): value is CachedRouteValueForMultipleVersions => + value.kind === 'ROUTE' || value.kind === 'APP_ROUTE' + +type MapArgsOrReturn = T extends readonly unknown[] + ? { [K in keyof T]: MapArgsOrReturn } + : T extends Promise + ? Promise> + : IncrementalCacheValueToMultipleVersions + +type MapCacheHandlerClassMethod = T extends (...args: infer Args) => infer Ret + ? (...args: MapArgsOrReturn) => MapArgsOrReturn + : T + +type MapCacheHandlerClass = { [K in keyof T]: MapCacheHandlerClassMethod } + +export type CacheHandlerForMultipleVersions = MapCacheHandlerClass diff --git a/tests/prepare.mjs b/tests/prepare.mjs index 13beeaca2a..5bc35c6f1f 100644 --- a/tests/prepare.mjs +++ b/tests/prepare.mjs @@ -44,14 +44,17 @@ const promises = fixtures.map((fixture) => }) } - // npm is the default - let cmd = `npm install --no-audit --progress=false --prefer-offline --legacy-peer-deps` + let cmd = `` const { packageManager } = JSON.parse(await readFile(join(cwd, 'package.json'), 'utf8')) if (packageManager?.startsWith('pnpm')) { // We disable frozen-lockfile because we may have changed the version of Next.js cmd = `pnpm install --frozen-lockfile=false ${ process.env.DEBUG || NEXT_VERSION !== 'latest' ? '' : '--reporter silent' }` + } else { + // npm is the default + cmd = `npm install --no-audit --progress=false --prefer-offline --legacy-peer-deps` + await rm(join(cwd, 'package-lock.json'), { force: true }) } const addPrefix = new Transform({ diff --git a/tests/utils/fixture.ts b/tests/utils/fixture.ts index 18ebb328fd..db86b9ea32 100644 --- a/tests/utils/fixture.ts +++ b/tests/utils/fixture.ts @@ -89,6 +89,10 @@ export const createFixture = async (fixture: string, ctx: FixtureTestContext) => ) { // @ts-ignore fetch doesn't have _nextOriginalFetch property in types globalThis.fetch = globalThis._nextOriginalFetch || globalThis.fetch._nextOriginalFetch + // https://github.com/vercel/next.js/pull/68193/files#diff-4c54e369ddb9a2db1eed95fe1d678f94c8e82c540204475d42c78e49bf4f223aR37-R40 + // above changed the way Next.js checks wether fetch was already patched. It still sets `__nextPatched` and `_nextOriginalFetch` + // properties we check above and use to get original fetch back + delete globalThis[Symbol.for('next-patch')] } ctx.cwd = await mkdtemp(join(tmpdir(), 'netlify-next-runtime-')) diff --git a/tests/utils/next-version-helpers.mjs b/tests/utils/next-version-helpers.mjs index abf4053471..f9b158e85a 100644 --- a/tests/utils/next-version-helpers.mjs +++ b/tests/utils/next-version-helpers.mjs @@ -4,12 +4,11 @@ import { readFile, writeFile } from 'node:fs/promises' import fg from 'fast-glob' import { gte, satisfies, valid } from 'semver' +import { execaCommand } from 'execa' const FUTURE_NEXT_PATCH_VERSION = '14.999.0' const NEXT_VERSION_REQUIRES_REACT_19 = '14.3.0-canary.45' -// TODO: Update this when React 19 is released -const REACT_19_VERSION = '19.0.0-rc.0' const REACT_18_VERSION = '18.2.0' /** @@ -97,8 +96,17 @@ export async function setNextVersionInFixture( return } packageJson.dependencies.next = version + + const { stdout } = await execaCommand( + `npm info next@${resolvedVersion} peerDependencies --json`, + { cwd }, + ) + + const nextPeerDependencies = JSON.parse(stdout) + if (updateReact && nextVersionRequiresReact19(checkVersion)) { - const reactVersion = operation === 'update' ? REACT_19_VERSION : REACT_18_VERSION + const reactVersion = + operation === 'update' ? nextPeerDependencies['react'] : REACT_18_VERSION packageJson.dependencies.react = reactVersion packageJson.dependencies['react-dom'] = reactVersion } From f13f3fae92c6b71e37cc71624a456f65b942a787 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 30 Aug 2024 22:23:13 +0200 Subject: [PATCH 4/9] test: add new version of patch for recent changes in next repository and try it if older one doesn't work (#2578) --- .github/workflows/test-e2e.yml | 2 +- run-local-test.sh | 2 +- tests/e2e-utils-v2.patch | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 tests/e2e-utils-v2.patch diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 29975aeaef..af388ee528 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -150,7 +150,7 @@ jobs: - name: patch Next.js run: | cp ../${{ env.runtime-path }}/tests/netlify-deploy.ts test/lib/next-modes/ - git apply ../${{ env.runtime-path }}/tests/e2e-utils.patch + git apply ../${{ env.runtime-path }}/tests/e2e-utils.patch || git apply ../${{ env.runtime-path }}/tests/e2e-utils-v2.patch working-directory: ${{ env.next-path }} - name: install Next.js diff --git a/run-local-test.sh b/run-local-test.sh index 4e61cf057b..4cfa139e8c 100755 --- a/run-local-test.sh +++ b/run-local-test.sh @@ -15,7 +15,7 @@ export NEXT_TEST_MODE=deploy export RUNTIME_DIR=$(pwd) cp tests/netlify-deploy.ts ../next.js/test/lib/next-modes/netlify-deploy.ts cd ../next.js/ -git apply ../next-runtime/tests/e2e-utils.patch +git apply ../next-runtime/tests/e2e-utils.patch || git apply ../next-runtime/tests/e2e-utils-v2.patch node run-tests.js --type e2e --debug --test-pattern $1 git checkout -- test/lib/e2e-utils.ts diff --git a/tests/e2e-utils-v2.patch b/tests/e2e-utils-v2.patch new file mode 100644 index 0000000000..65f118e33f --- /dev/null +++ b/tests/e2e-utils-v2.patch @@ -0,0 +1,13 @@ +diff --git a/test/lib/e2e-utils/index.ts b/test/lib/e2e-utils/index.ts +index 06765e81d9..a7a5a9e4bd 100644 +--- a/test/lib/e2e-utils/index.ts ++++ b/test/lib/e2e-utils/index.ts +@@ -5,7 +5,7 @@ import { PHASE_DEVELOPMENT_SERVER } from 'next/constants' + import { NextInstance, NextInstanceOpts } from '../next-modes/base' + import { NextDevInstance } from '../next-modes/next-dev' + import { NextStartInstance } from '../next-modes/next-start' +-import { NextDeployInstance } from '../next-modes/next-deploy' ++import { NextDeployInstance } from '../next-modes/netlify-deploy' + import { shouldRunTurboDevTest } from '../next-test-utils' + + export type { NextInstance } From fbc7e5bcdb7f2363be8ab499e9d309f96930a48c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 02:04:34 +0000 Subject: [PATCH 5/9] chore(deps): update dependency @types/node to v20.16.3 (#2580) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index b26cb35d69..14415ed314 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7022,9 +7022,9 @@ } }, "node_modules/@types/node": { - "version": "20.16.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.2.tgz", - "integrity": "sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==", + "version": "20.16.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.3.tgz", + "integrity": "sha512-/wdGiWRkMOm53gAsSyFMXFZHbVg7C6CbkrzHNpaHoYfsUWPg7m6ZRKtvQjgvQ9i8WT540a3ydRlRQbxjY30XxQ==", "dev": true, "dependencies": { "undici-types": "~6.19.2" @@ -24845,9 +24845,9 @@ } }, "@types/node": { - "version": "20.16.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.2.tgz", - "integrity": "sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==", + "version": "20.16.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.3.tgz", + "integrity": "sha512-/wdGiWRkMOm53gAsSyFMXFZHbVg7C6CbkrzHNpaHoYfsUWPg7m6ZRKtvQjgvQ9i8WT540a3ydRlRQbxjY30XxQ==", "dev": true, "requires": { "undici-types": "~6.19.2" From da7b73a1f5542d9a53d412df5ba71d81be3ab84e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 02:20:56 +0000 Subject: [PATCH 6/9] chore(deps): update dependency cheerio to v1.0.0 (#2582) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 199 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 184 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 14415ed314..fe6de09dae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8616,21 +8616,25 @@ } }, "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", "dev": true, "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">=18.17" }, "funding": { "url": "https://github.com/cheeriojs/cheerio?sponsor=1" @@ -8653,6 +8657,25 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cheerio/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -9843,6 +9866,19 @@ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", "dev": true }, + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "dev": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -12980,6 +13016,18 @@ "node": ">=10.18" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -16135,6 +16183,18 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", @@ -17465,6 +17525,12 @@ "node": ">=10" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "node_modules/scheduler": { "version": "0.25.0-rc.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc.0.tgz", @@ -18705,6 +18771,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", + "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", + "dev": true, + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -19554,6 +19629,27 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -25984,18 +26080,36 @@ } }, "cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", "dev": true, "requires": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "dependencies": { + "htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + } } }, "cheerio-select": { @@ -26885,6 +26999,16 @@ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", "dev": true }, + "encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "dev": true, + "requires": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + } + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -29059,6 +29183,15 @@ "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", "dev": true }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -31329,6 +31462,15 @@ "parse5": "^7.0.0" } }, + "parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "requires": { + "parse5": "^7.0.0" + } + }, "path-exists": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", @@ -32276,6 +32418,12 @@ "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", "dev": true }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "scheduler": { "version": "0.25.0-rc.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc.0.tgz", @@ -33216,6 +33364,12 @@ "which-boxed-primitive": "^1.0.2" } }, + "undici": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", + "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", + "dev": true + }, "undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -33674,6 +33828,21 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "dev": true }, + "whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "requires": { + "iconv-lite": "0.6.3" + } + }, + "whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true + }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", From b54b682db7fb92242175482c5d9fc201c9899d63 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 6 Sep 2024 15:06:42 +0200 Subject: [PATCH 7/9] fix: generate correct import path when 'src' directory is used and middleware imports wasm module (#2583) * test: add fixture using wasm in middleware in src directory mode * fix: generate correct import path when 'src' directory is used and middleware imports wasm module --- src/build/functions/edge.ts | 22 ++++++++++--- tests/fixtures/wasm-src/next.config.js | 30 ++++++++++++++++++ tests/fixtures/wasm-src/package.json | 16 ++++++++++ tests/fixtures/wasm-src/src/add.wasm | Bin 0 -> 126 bytes .../wasm-src/src/app/og-node/route.js | 8 +++++ tests/fixtures/wasm-src/src/app/og/route.js | 10 ++++++ tests/fixtures/wasm-src/src/middleware.js | 16 ++++++++++ .../src/pages/api/og-wrong-runtime.js | 22 +++++++++++++ tests/fixtures/wasm-src/src/pages/api/og.js | 26 +++++++++++++++ tests/fixtures/wasm-src/src/pages/index.js | 3 ++ tests/integration/wasm.test.ts | 9 ++++-- 11 files changed, 154 insertions(+), 8 deletions(-) create mode 100644 tests/fixtures/wasm-src/next.config.js create mode 100644 tests/fixtures/wasm-src/package.json create mode 100755 tests/fixtures/wasm-src/src/add.wasm create mode 100644 tests/fixtures/wasm-src/src/app/og-node/route.js create mode 100644 tests/fixtures/wasm-src/src/app/og/route.js create mode 100644 tests/fixtures/wasm-src/src/middleware.js create mode 100644 tests/fixtures/wasm-src/src/pages/api/og-wrong-runtime.js create mode 100644 tests/fixtures/wasm-src/src/pages/api/og.js create mode 100644 tests/fixtures/wasm-src/src/pages/index.js diff --git a/src/build/functions/edge.ts b/src/build/functions/edge.ts index 6d9919c11f..f6d331ddb6 100644 --- a/src/build/functions/edge.ts +++ b/src/build/functions/edge.ts @@ -1,5 +1,6 @@ import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises' -import { dirname, join } from 'node:path' +import { dirname, join, relative, sep } from 'node:path' +import { sep as posixSep } from 'node:path/posix' import type { Manifest, ManifestFunction } from '@netlify/edge-functions' import { glob } from 'fast-glob' @@ -8,6 +9,8 @@ import { pathToRegexp } from 'path-to-regexp' import { EDGE_HANDLER_NAME, PluginContext } from '../plugin-context.js' +const toPosixPath = (path: string) => path.split(sep).join(posixSep) + const writeEdgeManifest = async (ctx: PluginContext, manifest: Manifest) => { await mkdir(ctx.edgeFunctionsDir, { recursive: true }) await writeFile(join(ctx.edgeFunctionsDir, 'manifest.json'), JSON.stringify(manifest, null, 2)) @@ -107,10 +110,19 @@ const copyHandlerDependencies = async ( const parts = [shim] + const outputFile = join(destDir, `server/${name}.js`) + if (wasm?.length) { - parts.push( - `import { decode as _base64Decode } from "../edge-runtime/vendor/deno.land/std@0.175.0/encoding/base64.ts";`, + const base64ModulePath = join( + destDir, + 'edge-runtime/vendor/deno.land/std@0.175.0/encoding/base64.ts', ) + + const base64ModulePathRelativeToOutputFile = toPosixPath( + relative(dirname(outputFile), base64ModulePath), + ) + + parts.push(`import { decode as _base64Decode } from "${base64ModulePathRelativeToOutputFile}";`) for (const wasmChunk of wasm ?? []) { const data = await readFile(join(srcDir, wasmChunk.filePath)) parts.push( @@ -126,9 +138,9 @@ const copyHandlerDependencies = async ( parts.push(`;// Concatenated file: ${file} \n`, entrypoint) } const exports = `const middlewareEntryKey = Object.keys(_ENTRIES).find(entryKey => entryKey.startsWith("middleware_${name}")); export default _ENTRIES[middlewareEntryKey].default;` - await mkdir(dirname(join(destDir, `server/${name}.js`)), { recursive: true }) + await mkdir(dirname(outputFile), { recursive: true }) - await writeFile(join(destDir, `server/${name}.js`), [...parts, exports].join('\n')) + await writeFile(outputFile, [...parts, exports].join('\n')) } const createEdgeHandler = async (ctx: PluginContext, definition: NextDefinition): Promise => { diff --git a/tests/fixtures/wasm-src/next.config.js b/tests/fixtures/wasm-src/next.config.js new file mode 100644 index 0000000000..f7630c0194 --- /dev/null +++ b/tests/fixtures/wasm-src/next.config.js @@ -0,0 +1,30 @@ +const { platform } = require('process') +const fsPromises = require('fs/promises') + +// Next.js uses `fs.promises.copyFile` to copy files from `.next`to the `.next/standalone` directory +// It tries copying the same file twice in parallel. Unix is fine with that, but Windows fails +// with "Resource busy or locked", failing the build. +// We work around this by memoizing the copy operation, so that the second copy is a no-op. +// Tracked in TODO: report to Next.js folks +if (platform === 'win32') { + const copies = new Map() + + const originalCopy = fsPromises.copyFile + fsPromises.copyFile = (src, dest, mode) => { + const key = `${dest}:${src}` + const existingCopy = copies.get(key) + if (existingCopy) return existingCopy + + const copy = originalCopy(src, dest, mode) + copies.set(key, copy) + return copy + } +} + +/** @type {import('next').NextConfig} */ +module.exports = { + output: 'standalone', + eslint: { + ignoreDuringBuilds: true, + }, +} diff --git a/tests/fixtures/wasm-src/package.json b/tests/fixtures/wasm-src/package.json new file mode 100644 index 0000000000..d5a533479f --- /dev/null +++ b/tests/fixtures/wasm-src/package.json @@ -0,0 +1,16 @@ +{ + "name": "og-api", + "version": "0.1.0", + "private": true, + "scripts": { + "postinstall": "next build", + "dev": "next dev", + "build": "next build" + }, + "dependencies": { + "@vercel/og": "latest", + "next": "latest", + "react": "18.2.0", + "react-dom": "18.2.0" + } +} diff --git a/tests/fixtures/wasm-src/src/add.wasm b/tests/fixtures/wasm-src/src/add.wasm new file mode 100755 index 0000000000000000000000000000000000000000..f22496d0b6c87704a3844134af2a9fad47fa344c GIT binary patch literal 126 zcmY+)F%E)25CzcxcYuwog{_@8@C=+}7_*ZYlLaC+R?E>mnzU4}d9bw*08e2AMpjml z05&ZblC2Pz?kbhTw*8PQj>db_6)*Gq8<13=Zi_x_bz!fX?PKawmJlsxohJwTbJ%CZ I4Fg~44-%gmga7~l literal 0 HcmV?d00001 diff --git a/tests/fixtures/wasm-src/src/app/og-node/route.js b/tests/fixtures/wasm-src/src/app/og-node/route.js new file mode 100644 index 0000000000..39638abb96 --- /dev/null +++ b/tests/fixtures/wasm-src/src/app/og-node/route.js @@ -0,0 +1,8 @@ +import { ImageResponse } from '@vercel/og' + +export async function GET() { + return new ImageResponse(
hi
, { + width: 1200, + height: 630, + }) +} diff --git a/tests/fixtures/wasm-src/src/app/og/route.js b/tests/fixtures/wasm-src/src/app/og/route.js new file mode 100644 index 0000000000..9304ca61e7 --- /dev/null +++ b/tests/fixtures/wasm-src/src/app/og/route.js @@ -0,0 +1,10 @@ +import { ImageResponse } from '@vercel/og' + +export async function GET() { + return new ImageResponse(
hi
, { + width: 1200, + height: 630, + }) +} + +export const runtime = 'edge' diff --git a/tests/fixtures/wasm-src/src/middleware.js b/tests/fixtures/wasm-src/src/middleware.js new file mode 100644 index 0000000000..3173be4c63 --- /dev/null +++ b/tests/fixtures/wasm-src/src/middleware.js @@ -0,0 +1,16 @@ +import wasm from './add.wasm?module' +const instance$ = WebAssembly.instantiate(wasm) + +async function increment(a) { + const { instance } = await instance$ + return instance.exports.add_one(a) +} +export default async function middleware(request) { + const input = Number(request.nextUrl.searchParams.get('input')) || 1 + const value = await increment(input) + return new Response(null, { headers: { data: JSON.stringify({ input, value }) } }) +} + +export const config = { + matcher: '/wasm', +} diff --git a/tests/fixtures/wasm-src/src/pages/api/og-wrong-runtime.js b/tests/fixtures/wasm-src/src/pages/api/og-wrong-runtime.js new file mode 100644 index 0000000000..a693c6f5df --- /dev/null +++ b/tests/fixtures/wasm-src/src/pages/api/og-wrong-runtime.js @@ -0,0 +1,22 @@ +// /pages/api/og.jsx +import { ImageResponse } from '@vercel/og' + +export default function () { + return new ImageResponse( + ( +
+ Hello! +
+ ), + ) +} diff --git a/tests/fixtures/wasm-src/src/pages/api/og.js b/tests/fixtures/wasm-src/src/pages/api/og.js new file mode 100644 index 0000000000..55ab54d2c1 --- /dev/null +++ b/tests/fixtures/wasm-src/src/pages/api/og.js @@ -0,0 +1,26 @@ +// /pages/api/og.jsx +import { ImageResponse } from '@vercel/og' + +export const config = { + runtime: 'edge', +} + +export default function () { + return new ImageResponse( + ( +
+ Hello! +
+ ), + ) +} diff --git a/tests/fixtures/wasm-src/src/pages/index.js b/tests/fixtures/wasm-src/src/pages/index.js new file mode 100644 index 0000000000..ff7159d914 --- /dev/null +++ b/tests/fixtures/wasm-src/src/pages/index.js @@ -0,0 +1,3 @@ +export default function Page() { + return

hello world

+} diff --git a/tests/integration/wasm.test.ts b/tests/integration/wasm.test.ts index fcad0dfbe6..679117de61 100644 --- a/tests/integration/wasm.test.ts +++ b/tests/integration/wasm.test.ts @@ -21,7 +21,10 @@ beforeEach(async (ctx) => { await startMockBlobStore(ctx) }) -describe('WASM', () => { +describe.each([ + { fixture: 'wasm', edgeHandlerFunction: '___netlify-edge-handler-middleware' }, + { fixture: 'wasm-src', edgeHandlerFunction: '___netlify-edge-handler-src-middleware' }, +])('$fixture', ({ fixture, edgeHandlerFunction }) => { beforeEach(async (ctx) => { // set for each test a new deployID and siteID ctx.deployID = generateRandomObjectID() @@ -33,7 +36,7 @@ describe('WASM', () => { await startMockBlobStore(ctx) - await createFixture('wasm', ctx) + await createFixture(fixture, ctx) await runPlugin(ctx) }) @@ -58,7 +61,7 @@ describe('WASM', () => { test('should work in middleware', async (ctx) => { const origin = new LocalServer() const response = await invokeEdgeFunction(ctx, { - functions: ['___netlify-edge-handler-middleware'], + functions: [edgeHandlerFunction], origin, url: '/wasm?input=3', }) From 74693697050a8653dca021b24c58eec72effbeb0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 13:23:55 +0000 Subject: [PATCH 8/9] chore(deps): update dependency @netlify/serverless-functions-api to ^1.23.0 (#2584) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index fe6de09dae..3f95f389a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@netlify/edge-functions": "^2.10.0", "@netlify/eslint-config-node": "^7.0.1", "@netlify/functions": "^2.8.1", - "@netlify/serverless-functions-api": "^1.22.0", + "@netlify/serverless-functions-api": "^1.23.0", "@netlify/zip-it-and-ship-it": "^9.37.3", "@opentelemetry/api": "^1.8.0", "@opentelemetry/exporter-trace-otlp-http": "^0.51.0", @@ -4980,9 +4980,9 @@ "dev": true }, "node_modules/@netlify/serverless-functions-api": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-1.22.0.tgz", - "integrity": "sha512-vv8fWCOIadSvdmR+8UYopdyHO/gOysl+8IBOxUUB0B3y7nnLOiBniE1JBeBR3y7gI/q/cnibBF2RhR3W04Wo/A==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-1.23.0.tgz", + "integrity": "sha512-0SwlMeqcwPzvRgE6aJczAiEz27hXl4thd46PECN3bX3FvAEuqpSTjQefuF3PEycdnWhCvTAKe1OW+Uv7ZT3pug==", "dev": true, "dependencies": { "@netlify/node-cookies": "^0.1.0", @@ -23491,9 +23491,9 @@ } }, "@netlify/serverless-functions-api": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-1.22.0.tgz", - "integrity": "sha512-vv8fWCOIadSvdmR+8UYopdyHO/gOysl+8IBOxUUB0B3y7nnLOiBniE1JBeBR3y7gI/q/cnibBF2RhR3W04Wo/A==", + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@netlify/serverless-functions-api/-/serverless-functions-api-1.23.0.tgz", + "integrity": "sha512-0SwlMeqcwPzvRgE6aJczAiEz27hXl4thd46PECN3bX3FvAEuqpSTjQefuF3PEycdnWhCvTAKe1OW+Uv7ZT3pug==", "dev": true, "requires": { "@netlify/node-cookies": "^0.1.0", diff --git a/package.json b/package.json index ac8acfdef2..aef599ba76 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@netlify/edge-functions": "^2.10.0", "@netlify/eslint-config-node": "^7.0.1", "@netlify/functions": "^2.8.1", - "@netlify/serverless-functions-api": "^1.22.0", + "@netlify/serverless-functions-api": "^1.23.0", "@netlify/zip-it-and-ship-it": "^9.37.3", "@opentelemetry/api": "^1.8.0", "@opentelemetry/exporter-trace-otlp-http": "^0.51.0", From eb9350b36f1960134107266ce4dcf1099a1cbe62 Mon Sep 17 00:00:00 2001 From: "token-generator-app[bot]" <82042599+token-generator-app[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 16:02:37 +0200 Subject: [PATCH 9/9] chore(main): release 5.7.1 (#2577) Co-authored-by: token-generator-app[bot] <82042599+token-generator-app[bot]@users.noreply.github.com> Co-authored-by: Michal Piechowiak --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 68f9449d5d..cabdafcbcd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "5.7.0" + ".": "5.7.1" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 66f06efec3..752eb2c091 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [5.7.1](https://github.com/netlify/next-runtime/compare/v5.7.0...v5.7.1) (2024-09-06) + + +### Bug Fixes + +* generate correct import path when 'src' directory is used and middleware imports wasm module ([#2583](https://github.com/netlify/next-runtime/issues/2583)) ([b54b682](https://github.com/netlify/next-runtime/commit/b54b682db7fb92242175482c5d9fc201c9899d63)) +* update cache handler to accommodate changes in next@canary ([#2572](https://github.com/netlify/next-runtime/issues/2572)) ([83f685e](https://github.com/netlify/next-runtime/commit/83f685e15552cee1c8a4d6c68251c01ff1cfe3fb)) + ## [5.7.0](https://github.com/netlify/next-runtime/compare/v5.6.0...v5.7.0) (2024-08-26) diff --git a/package-lock.json b/package-lock.json index 3f95f389a4..ef282e3f91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@netlify/plugin-nextjs", - "version": "5.7.0", + "version": "5.7.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@netlify/plugin-nextjs", - "version": "5.7.0", + "version": "5.7.1", "license": "MIT", "devDependencies": { "@fastly/http-compute-js": "1.1.4", diff --git a/package.json b/package.json index aef599ba76..04e9fadfb0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@netlify/plugin-nextjs", - "version": "5.7.0", + "version": "5.7.1", "description": "Run Next.js seamlessly on Netlify", "main": "./dist/index.js", "type": "module",