diff --git a/src/build/content/server.ts b/src/build/content/server.ts index 8aa2287be4..ffa5dbbd4c 100644 --- a/src/build/content/server.ts +++ b/src/build/content/server.ts @@ -106,7 +106,7 @@ export const copyNextServerCode = async (ctx: PluginContext): Promise => { `server/*`, `server/chunks/**/*`, `server/edge-chunks/**/*`, - `server/edge/chunks/**/*`, + `server/edge/**/*`, `server/+(app|pages)/**/*.js`, ], { @@ -291,6 +291,8 @@ async function patchNextModules( export const copyNextDependencies = async (ctx: PluginContext): Promise => { await tracer.withActiveSpan('copyNextDependencies', async () => { const entries = await readdir(ctx.standaloneDir) + const filter = ctx.constants.IS_LOCAL ? undefined : nodeModulesFilter + const promises: Promise[] = entries.map(async (entry) => { // copy all except the distDir (.next) folder as this is handled in a separate function // this will include the node_modules folder as well @@ -299,7 +301,6 @@ export const copyNextDependencies = async (ctx: PluginContext): Promise => } const src = join(ctx.standaloneDir, entry) const dest = join(ctx.serverHandlerDir, entry) - const filter = ctx.constants.IS_LOCAL ? undefined : nodeModulesFilter await cp(src, dest, { recursive: true, verbatimSymlinks: true, @@ -321,7 +322,7 @@ export const copyNextDependencies = async (ctx: PluginContext): Promise => // see: https://github.com/vercel/next.js/issues/50072 if (existsSync(rootSrcDir) && ctx.standaloneRootDir !== ctx.standaloneDir) { promises.push( - cp(rootSrcDir, rootDestDir, { recursive: true, verbatimSymlinks: true }).then(() => + cp(rootSrcDir, rootDestDir, { recursive: true, verbatimSymlinks: true, filter }).then(() => recreateNodeModuleSymlinks(resolve('node_modules'), rootDestDir), ), ) diff --git a/tests/e2e/cli-before-regional-blobs-support.test.ts b/tests/e2e/cli-before-regional-blobs-support.test.ts index e4779339f8..c6f7193e01 100644 --- a/tests/e2e/cli-before-regional-blobs-support.test.ts +++ b/tests/e2e/cli-before-regional-blobs-support.test.ts @@ -17,7 +17,7 @@ test('should serve 404 page when requesting non existing page (no matching route const headers = response?.headers() || {} expect(response?.status()).toBe(404) - expect(await page.textContent('h1')).toBe('404') + await expect(page.locator('h1')).toHaveText('404') // https://github.com/vercel/next.js/pull/69802 made changes to returned cache-control header, // after that (14.2.10 and canary.147) 404 pages would have `private` directive, before that it diff --git a/tests/e2e/middleware.test.ts b/tests/e2e/middleware.test.ts index a25de675a4..369d65f2a1 100644 --- a/tests/e2e/middleware.test.ts +++ b/tests/e2e/middleware.test.ts @@ -246,6 +246,11 @@ for (const { expectedRuntime, isNodeMiddleware, label, testWithSwitchableMiddlew const pageResponse = await page.goto(`${edgeOrNodeMiddlewarePages.url}/link`) expect(await pageResponse?.headerValue('x-runtime')).toEqual(expectedRuntime) + // wait for hydration to finish before doing client navigation + await expect(page.getByTestId('hydration')).toHaveText('hydrated', { + timeout: 10_000, + }) + await page.evaluate(() => { // set some value to window to check later if browser did reload and lost this state ;(window as ExtendedWindow).didReload = false @@ -305,6 +310,11 @@ for (const { expectedRuntime, isNodeMiddleware, label, testWithSwitchableMiddlew ) expect(await pageResponse?.headerValue('x-runtime')).toEqual(expectedRuntime) + // wait for hydration to finish before doing client navigation + await expect(page.getByTestId('hydration')).toHaveText('hydrated', { + timeout: 10_000, + }) + await page.evaluate(() => { // set some value to window to check later if browser did reload and lost this state ;(window as ExtendedWindow).didReload = false diff --git a/tests/e2e/on-demand-app.test.ts b/tests/e2e/on-demand-app.test.ts index 13c0ee3dd2..b4e6ea63aa 100644 --- a/tests/e2e/on-demand-app.test.ts +++ b/tests/e2e/on-demand-app.test.ts @@ -96,9 +96,9 @@ test.describe('app router on-demand revalidation', () => { : 's-maxage=31536000, stale-while-revalidate=31536000, durable', ) - const date1 = await page.textContent('[data-testid="date-now"]') + const date1 = await page.getByTestId('date-now').textContent() - const h1 = await page.textContent('h1') + const h1 = await page.locator('h1').textContent() expect(h1).toBe(expectedH1Content) const response2 = await pollUntilHeadersMatch(new URL(pagePath, serverComponents.url).href, { @@ -127,7 +127,7 @@ test.describe('app router on-demand revalidation', () => { ) // the page is cached - const date2 = await page.textContent('[data-testid="date-now"]') + const date2 = await page.getByTestId('date-now').textContent() expect(date2).toBe(date1) const revalidate = await page.goto(new URL(revalidateApiPath, serverComponents.url).href) @@ -159,7 +159,7 @@ test.describe('app router on-demand revalidation', () => { ) // the page has now an updated date - const date3 = await page.textContent('[data-testid="date-now"]') + const date3 = await page.getByTestId('date-now').textContent() expect(date3).not.toBe(date2) const response4 = await pollUntilHeadersMatch(new URL(pagePath, serverComponents.url).href, { @@ -188,7 +188,7 @@ test.describe('app router on-demand revalidation', () => { ) // the page is cached - const date4 = await page.textContent('[data-testid="date-now"]') + const date4 = await page.getByTestId('date-now').textContent() expect(date4).toBe(date3) }) } diff --git a/tests/e2e/page-router.test.ts b/tests/e2e/page-router.test.ts index d0c8f2ee11..759928c7b2 100644 --- a/tests/e2e/page-router.test.ts +++ b/tests/e2e/page-router.test.ts @@ -174,12 +174,12 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => { ) if (fallbackWasServed) { - const loading = await page.textContent('[data-testid="loading"]') + const loading = await page.getByTestId('loading').textContent() expect(loading, 'Fallback should be shown').toBe('Loading...') } - const date1 = await page.textContent('[data-testid="date-now"]') - const h1 = await page.textContent('h1') + const date1 = await page.getByTestId('date-now').textContent() + const h1 = await page.locator('h1').textContent() expect(h1).toBe(expectedH1Content) // check json route @@ -238,7 +238,7 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => { ) // the page is cached - const date2 = await page.textContent('[data-testid="date-now"]') + const date2 = await page.getByTestId('date-now').textContent() expect(date2).toBe(date1) // check json route @@ -299,7 +299,7 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => { expect(headers3?.['x-nextjs-cache']).toBeUndefined() // the page has now an updated date - const date3 = await page.textContent('[data-testid="date-now"]') + const date3 = await page.getByTestId('date-now').textContent() expect(date3).not.toBe(date2) // check json route @@ -366,7 +366,7 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => { }, ) expect(response1?.status()).toBe(200) - const date1 = (await page.textContent('[data-testid="date-now"]')) ?? '' + const date1 = (await page.getByTestId('date-now').textContent()) ?? '' // ensure response was produced before invocation (served from cache) expect(date1.localeCompare(beforeFetch)).toBeLessThan(0) @@ -391,7 +391,7 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => { }, ) expect(response2?.status()).toBe(200) - const date2 = (await page.textContent('[data-testid="date-now"]')) ?? '' + const date2 = (await page.getByTestId('date-now').textContent()) ?? '' // ensure response was produced after initial invocation expect(beforeFetch.localeCompare(date2)).toBeLessThan(0) @@ -416,7 +416,7 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => { ) // ensure response was NOT produced before invocation - const date1 = (await page.textContent('[data-testid="date-now"]')) ?? '' + const date1 = (await page.getByTestId('date-now').textContent()) ?? '' expect(date1.localeCompare(beforeFirstFetch)).toBeGreaterThan(0) // allow page to get stale @@ -431,7 +431,7 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => { /s-maxage=60, stale-while-revalidate=[0-9]+, durable/, ) - const date2 = (await page.textContent('[data-testid="date-now"]')) ?? '' + const date2 = (await page.getByTestId('date-now').textContent()) ?? '' expect(date2).toBe(date1) // wait a bit to ensure background work has a chance to finish @@ -450,7 +450,7 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => { /s-maxage=60, stale-while-revalidate=[0-9]+, durable/, ) - const date3 = (await page.textContent('[data-testid="date-now"]')) ?? '' + const date3 = (await page.getByTestId('date-now').textContent()) ?? '' expect(date3.localeCompare(date2)).toBeGreaterThan(0) }) @@ -469,7 +469,7 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => { const headers = response?.headers() || {} expect(response?.status()).toBe(404) - expect(await page.textContent('p')).toBe('Custom 404 page') + await expect(page.getByTestId('custom-404')).toHaveText('Custom 404 page') // https://github.com/vercel/next.js/pull/69802 made changes to returned cache-control header, // after that (14.2.10 and canary.147) 404 pages would have `private` directive, before that @@ -493,7 +493,7 @@ test.describe('Simple Page Router (no basePath, no i18n)', () => { const headers = response?.headers() || {} expect(response?.status()).toBe(404) - expect(await page.textContent('p')).toBe('Custom 404 page') + await expect(page.getByTestId('custom-404')).toHaveText('Custom 404 page') expect(headers['debug-netlify-cdn-cache-control']).toBe( nextVersionSatisfies('>=15.0.0-canary.187') @@ -748,12 +748,12 @@ test.describe('Page Router with basePath and i18n', () => { ) if (fallbackWasServedImplicitLocale) { - const loading = await page.textContent('[data-testid="loading"]') + const loading = await page.getByTestId('loading').textContent() expect(loading, 'Fallback should be shown').toBe('Loading...') } - const date1ImplicitLocale = await page.textContent('[data-testid="date-now"]') - const h1ImplicitLocale = await page.textContent('h1') + const date1ImplicitLocale = await page.getByTestId('date-now').textContent() + const h1ImplicitLocale = await page.locator('h1').textContent() expect(h1ImplicitLocale).toBe(expectedH1Content) const response1ExplicitLocale = await pollUntilHeadersMatch( @@ -790,12 +790,12 @@ test.describe('Page Router with basePath and i18n', () => { ) if (fallbackWasServedExplicitLocale) { - const loading = await page.textContent('[data-testid="loading"]') + const loading = await page.getByTestId('loading').textContent() expect(loading, 'Fallback should be shown').toBe('Loading...') } - const date1ExplicitLocale = await page.textContent('[data-testid="date-now"]') - const h1ExplicitLocale = await page.textContent('h1') + const date1ExplicitLocale = await page.getByTestId('date-now').textContent() + const h1ExplicitLocale = await page.locator('h1').textContent() expect(h1ExplicitLocale).toBe(expectedH1Content) // implicit and explicit locale paths should be the same (same cached response) @@ -861,7 +861,7 @@ test.describe('Page Router with basePath and i18n', () => { ) // the page is cached - const date2ImplicitLocale = await page.textContent('[data-testid="date-now"]') + const date2ImplicitLocale = await page.getByTestId('date-now').textContent() expect(date2ImplicitLocale).toBe(date1ImplicitLocale) const response2ExplicitLocale = await pollUntilHeadersMatch( @@ -893,7 +893,7 @@ test.describe('Page Router with basePath and i18n', () => { ) // the page is cached - const date2ExplicitLocale = await page.textContent('[data-testid="date-now"]') + const date2ExplicitLocale = await page.getByTestId('date-now').textContent() expect(date2ExplicitLocale).toBe(date1ExplicitLocale) // check json route @@ -961,7 +961,7 @@ test.describe('Page Router with basePath and i18n', () => { expect(headers3ImplicitLocale?.['x-nextjs-cache']).toBeUndefined() // the page has now an updated date - const date3ImplicitLocale = await page.textContent('[data-testid="date-now"]') + const date3ImplicitLocale = await page.getByTestId('date-now').textContent() expect(date3ImplicitLocale).not.toBe(date2ImplicitLocale) const response3ExplicitLocale = await pollUntilHeadersMatch( @@ -984,7 +984,7 @@ test.describe('Page Router with basePath and i18n', () => { expect(headers3ExplicitLocale?.['x-nextjs-cache']).toBeUndefined() // the page has now an updated date - const date3ExplicitLocale = await page.textContent('[data-testid="date-now"]') + const date3ExplicitLocale = await page.getByTestId('date-now').textContent() expect(date3ExplicitLocale).not.toBe(date2ExplicitLocale) // implicit and explicit locale paths should be the same (same cached response) @@ -1057,7 +1057,7 @@ test.describe('Page Router with basePath and i18n', () => { expect(headers4ImplicitLocale?.['x-nextjs-cache']).toBeUndefined() // the page has now an updated date - const date4ImplicitLocale = await page.textContent('[data-testid="date-now"]') + const date4ImplicitLocale = await page.getByTestId('date-now').textContent() expect(date4ImplicitLocale).not.toBe(date3ImplicitLocale) const response4ExplicitLocale = await pollUntilHeadersMatch( @@ -1080,7 +1080,7 @@ test.describe('Page Router with basePath and i18n', () => { expect(headers4ExplicitLocale?.['x-nextjs-cache']).toBeUndefined() // the page has now an updated date - const date4ExplicitLocale = await page.textContent('[data-testid="date-now"]') + const date4ExplicitLocale = await page.getByTestId('date-now').textContent() expect(date4ExplicitLocale).not.toBe(date3ExplicitLocale) // implicit and explicit locale paths should be the same (same cached response) @@ -1173,12 +1173,12 @@ test.describe('Page Router with basePath and i18n', () => { ) if (fallbackWasServed) { - const loading = await page.textContent('[data-testid="loading"]') + const loading = await page.getByTestId('loading').textContent() expect(loading, 'Fallback should be shown').toBe('Loading...') } - const date1 = await page.textContent('[data-testid="date-now"]') - const h1 = await page.textContent('h1') + const date1 = await page.getByTestId('date-now').textContent() + const h1 = await page.locator('h1').textContent() expect(h1).toBe(expectedH1Content) // check json route @@ -1241,7 +1241,7 @@ test.describe('Page Router with basePath and i18n', () => { ) // the page is cached - const date2 = await page.textContent('[data-testid="date-now"]') + const date2 = await page.getByTestId('date-now').textContent() expect(date2).toBe(date1) // check json route @@ -1309,7 +1309,7 @@ test.describe('Page Router with basePath and i18n', () => { expect(headers3?.['x-nextjs-cache']).toBeUndefined() // the page has now an updated date - const date3 = await page.textContent('[data-testid="date-now"]') + const date3 = await page.getByTestId('date-now').textContent() expect(date3).not.toBe(date2) // check json route @@ -1360,7 +1360,7 @@ test.describe('Page Router with basePath and i18n', () => { const headers = response?.headers() || {} expect(response?.status()).toBe(404) - expect(await page.textContent('p')).toBe('Custom 404 page for locale: en') + await expect(page.getByTestId('custom-404')).toHaveText('Custom 404 page for locale: en') expect(headers['debug-netlify-cdn-cache-control']).toMatch( /no-cache, no-store, max-age=0, must-revalidate, durable/m, @@ -1378,7 +1378,7 @@ test.describe('Page Router with basePath and i18n', () => { const headers = response?.headers() || {} expect(response?.status()).toBe(404) - expect(await page.textContent('p')).toBe('Custom 404 page for locale: en') + await expect(page.getByTestId('custom-404')).toHaveText('Custom 404 page for locale: en') // Prior to v14.2.4 notFound pages are not cacheable // https://github.com/vercel/next.js/pull/66674 diff --git a/tests/e2e/simple-app.test.ts b/tests/e2e/simple-app.test.ts index fb790afcff..c9b4aea017 100644 --- a/tests/e2e/simple-app.test.ts +++ b/tests/e2e/simple-app.test.ts @@ -1,5 +1,5 @@ import { expect, type Locator, type Response } from '@playwright/test' -import { nextVersionSatisfies } from '../utils/next-version-helpers.mjs' +import { hasDefaultTurbopackBuilds, nextVersionSatisfies } from '../utils/next-version-helpers.mjs' import { test } from '../utils/playwright-helpers.js' const expectImageWasLoaded = async (locator: Locator) => { @@ -227,7 +227,7 @@ test('requesting a non existing page route that needs to be fetched from the blo const headers = response?.headers() || {} expect(response?.status()).toBe(404) - expect(await page.textContent('h1')).toBe('404 Not Found') + await expect(page.locator('h1')).toHaveText('404 Not Found') // https://github.com/vercel/next.js/pull/66674 made changes to returned cache-control header, // before that 404 page would have `private` directive, after that (14.2.4 and canary.24) it @@ -254,7 +254,7 @@ test('requesting a non existing page route that needs to be fetched from the blo const headers = response?.headers() || {} expect(response?.status()).toBe(404) - expect(await page.textContent('h1')).toBe('404 Not Found') + await expect(page.locator('h1')).toHaveText('404 Not Found') expect(headers['debug-netlify-cdn-cache-control']).toBe( nextVersionSatisfies('>=15.0.0-canary.187') @@ -273,6 +273,8 @@ test('Compressed rewrites are readable', async ({ simple }) => { }) test('can require CJS module that is not bundled', async ({ simple }) => { + // setup for this test only works with webpack builds due to usage of ` __non_webpack_require__` to avoid bundling a file + test.skip(hasDefaultTurbopackBuilds(), 'Setup for this test only works with webpack builds') const resp = await fetch(`${simple.url}/api/cjs-file-with-js-extension`) expect(resp.status).toBe(200) diff --git a/tests/e2e/turborepo.test.ts b/tests/e2e/turborepo.test.ts index 0475994571..e362536995 100644 --- a/tests/e2e/turborepo.test.ts +++ b/tests/e2e/turborepo.test.ts @@ -41,8 +41,8 @@ test.describe('[PNPM] Package manager', () => { : 's-maxage=31536000, stale-while-revalidate=31536000, durable', ) - const date1 = await page.textContent('[data-testid="date-now"]') - const h1 = await page.textContent('h1') + const date1 = await page.getByTestId('date-now').textContent() + const h1 = await page.locator('h1').textContent() expect(h1).toBe('Show #71') const response2 = await pollUntilHeadersMatch( @@ -74,7 +74,7 @@ test.describe('[PNPM] Package manager', () => { ) // the page is cached - const date2 = await page.textContent('[data-testid="date-now"]') + const date2 = await page.getByTestId('date-now').textContent() expect(date2).toBe(date1) const revalidate = await page.goto(new URL('/api/revalidate', turborepo.url).href) @@ -104,7 +104,7 @@ test.describe('[PNPM] Package manager', () => { expect(headers3?.['x-nextjs-cache']).toBeUndefined() // the page has now an updated date - const date3 = await page.textContent('[data-testid="date-now"]') + const date3 = await page.getByTestId('date-now').textContent() expect(date3).not.toBe(date2) }) }) @@ -149,8 +149,8 @@ test.describe('[NPM] Package manager', () => { : 's-maxage=31536000, stale-while-revalidate=31536000, durable', ) - const date1 = await page.textContent('[data-testid="date-now"]') - const h1 = await page.textContent('h1') + const date1 = await page.getByTestId('date-now').textContent() + const h1 = await page.locator('h1').textContent() expect(h1).toBe('Show #71') const response2 = await pollUntilHeadersMatch( @@ -182,7 +182,7 @@ test.describe('[NPM] Package manager', () => { ) // the page is cached - const date2 = await page.textContent('[data-testid="date-now"]') + const date2 = await page.getByTestId('date-now').textContent() expect(date2).toBe(date1) const revalidate = await page.goto(new URL('/api/revalidate', turborepoNPM.url).href) @@ -212,7 +212,7 @@ test.describe('[NPM] Package manager', () => { expect(headers3?.['x-nextjs-cache']).toBeUndefined() // the page has now an updated date - const date3 = await page.textContent('[data-testid="date-now"]') + const date3 = await page.getByTestId('date-now').textContent() expect(date3).not.toBe(date2) }) diff --git a/tests/fixtures/middleware-i18n/pages/link/index.js b/tests/fixtures/middleware-i18n/pages/link/index.js index 73699d73a1..e0d58ba263 100644 --- a/tests/fixtures/middleware-i18n/pages/link/index.js +++ b/tests/fixtures/middleware-i18n/pages/link/index.js @@ -1,6 +1,12 @@ +import { useState, useEffect } from 'react' import Link from 'next/link' export default function Page() { + const [isHydrated, setIsHydrated] = useState(false) + useEffect(() => { + setIsHydrated(true) + }, []) + return (

Page with Links

@@ -62,6 +68,7 @@ export default function Page() { +
{isHydrated ? 'hydrated' : 'hydrating'}
) } diff --git a/tests/fixtures/middleware-pages/pages/link/index.js b/tests/fixtures/middleware-pages/pages/link/index.js index 73699d73a1..e0d58ba263 100644 --- a/tests/fixtures/middleware-pages/pages/link/index.js +++ b/tests/fixtures/middleware-pages/pages/link/index.js @@ -1,6 +1,12 @@ +import { useState, useEffect } from 'react' import Link from 'next/link' export default function Page() { + const [isHydrated, setIsHydrated] = useState(false) + useEffect(() => { + setIsHydrated(true) + }, []) + return (

Page with Links

@@ -62,6 +68,7 @@ export default function Page() { +
{isHydrated ? 'hydrated' : 'hydrating'}
) } diff --git a/tests/fixtures/middleware/next.config.js b/tests/fixtures/middleware/next.config.js index 03c2828b32..54308b902a 100644 --- a/tests/fixtures/middleware/next.config.js +++ b/tests/fixtures/middleware/next.config.js @@ -19,6 +19,11 @@ const nextConfig = { return config }, + // turbopack becomes default for builds in Next 16. There is failure when webpack configuration is present + // without turbopack configuration, so we add a turbopack configuration here to ensure this fixture + // works with default build bundler for all tested versions + // see https://github.com/vercel/next.js/blob/ba5a0ca79944b4c8a59d80d677bfedaf0fef33d6/packages/next/src/lib/turbopack-warning.ts#L159-L177 + turbopack: {}, outputFileTracingRoot: __dirname, } diff --git a/tests/fixtures/nx-integrated/.gitignore b/tests/fixtures/nx-integrated/.gitignore index f44f4e80ba..ffa6719d96 100644 --- a/tests/fixtures/nx-integrated/.gitignore +++ b/tests/fixtures/nx-integrated/.gitignore @@ -39,9 +39,12 @@ testem.log Thumbs.db .nx/cache +.nx/workspace-data # Next.js .next # Local Netlify folder .netlify +.cursor/rules/nx-rules.mdc +.github/instructions/nx.instructions.md diff --git a/tests/fixtures/nx-integrated/nx.json b/tests/fixtures/nx-integrated/nx.json index 706a7a57a8..6e5eda2808 100644 --- a/tests/fixtures/nx-integrated/nx.json +++ b/tests/fixtures/nx-integrated/nx.json @@ -31,5 +31,6 @@ "linter": "eslint" } } - } + }, + "useInferencePlugins": false } diff --git a/tests/fixtures/nx-integrated/package.json b/tests/fixtures/nx-integrated/package.json index 814f4d924b..8635fc4801 100644 --- a/tests/fixtures/nx-integrated/package.json +++ b/tests/fixtures/nx-integrated/package.json @@ -13,18 +13,17 @@ "tslib": "^2.3.0" }, "devDependencies": { - "@nx/js": "17.3.0", - "@nx/next": "17.3.0", - "@nx/workspace": "17.3.0", - "@swc-node/register": "~1.6.7", - "@swc/core": "~1.3.85", - "@swc/helpers": "~0.5.2", + "@nx/js": "21.6.3", + "@nx/next": "21.6.3", + "@nx/workspace": "21.6.3", + "@swc-node/register": "1.9.2", + "@swc/core": "1.5.7", + "@swc/helpers": "0.5.17", "@types/node": "18.16.9", "@types/react": "18.2.33", "@types/react-dom": "18.2.14", - "nx": "17.3.0", + "nx": "21.6.3", "ts-node": "10.9.1", "typescript": "~5.3.2" - }, - "packageManager": "pnpm@8.9.0" + } } diff --git a/tests/fixtures/page-router-404-get-static-props-with-revalidate/pages/404.js b/tests/fixtures/page-router-404-get-static-props-with-revalidate/pages/404.js index cb047699af..d44d5f5d5b 100644 --- a/tests/fixtures/page-router-404-get-static-props-with-revalidate/pages/404.js +++ b/tests/fixtures/page-router-404-get-static-props-with-revalidate/pages/404.js @@ -1,6 +1,6 @@ export default function NotFound({ timestamp }) { return ( -

+

Custom 404 page with revalidate:

{timestamp}

) diff --git a/tests/fixtures/page-router-base-path-i18n/pages/404.js b/tests/fixtures/page-router-base-path-i18n/pages/404.js index 0f42ed27ed..75e2ba1607 100644 --- a/tests/fixtures/page-router-base-path-i18n/pages/404.js +++ b/tests/fixtures/page-router-base-path-i18n/pages/404.js @@ -1,6 +1,6 @@ export default function NotFound({ locale }) { return ( -

+

Custom 404 page for locale:

{locale}

) diff --git a/tests/fixtures/page-router/pages/404.js b/tests/fixtures/page-router/pages/404.js index 3c251e6665..a1c17694d1 100644 --- a/tests/fixtures/page-router/pages/404.js +++ b/tests/fixtures/page-router/pages/404.js @@ -1,3 +1,3 @@ export default function NotFound() { - return

Custom 404 page

+ return

Custom 404 page

} diff --git a/tests/fixtures/wasm-src/next.config.js b/tests/fixtures/wasm-src/next.config.js index 4263b7f9c2..07ebbcc865 100644 --- a/tests/fixtures/wasm-src/next.config.js +++ b/tests/fixtures/wasm-src/next.config.js @@ -1,5 +1,6 @@ const { platform } = require('process') const fsPromises = require('fs/promises') +const { satisfies } = require('semver') // 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 @@ -28,4 +29,27 @@ module.exports = { ignoreDuringBuilds: true, }, outputFileTracingRoot: __dirname, + // there is no single way to use `next/og` or `@vercel/og` depending on Next.js version + // - next@<14 doesn't have 'next/og' export + // - next turbopack builds doesn't work with `@vercel/og` + // so this adds `next-og-alias` alias depending on next version for both webpack and turbopack + // so we can test this in all the versions + webpack: (config) => { + const hasNextOg = !satisfies(require('next/package.json').version, '<14.0.0', { + includePrerelease: true, + }) + + if (!hasNextOg) { + config.resolve.alias['next-og-alias$'] = '@vercel/og' + } else { + config.resolve.alias['next-og-alias$'] = 'next/og' + } + + return config + }, + turbopack: { + resolveAlias: { + 'next-og-alias': 'next/og', + }, + }, } diff --git a/tests/fixtures/wasm-src/package.json b/tests/fixtures/wasm-src/package.json index d5a533479f..7cbbd4d7ba 100644 --- a/tests/fixtures/wasm-src/package.json +++ b/tests/fixtures/wasm-src/package.json @@ -11,6 +11,7 @@ "@vercel/og": "latest", "next": "latest", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "semver": "^7.7.2" } } diff --git a/tests/fixtures/wasm-src/src/app/og-node/route.js b/tests/fixtures/wasm-src/src/app/og-node/route.js index 6338e7e61b..2fa26189c2 100644 --- a/tests/fixtures/wasm-src/src/app/og-node/route.js +++ b/tests/fixtures/wasm-src/src/app/og-node/route.js @@ -1,4 +1,5 @@ -import { ImageResponse } from '@vercel/og' +// see next.config for details about 'next-og-alias' +import { ImageResponse } from 'next-og-alias' export async function GET() { return new ImageResponse(
hi
, { diff --git a/tests/fixtures/wasm-src/src/app/og/route.js b/tests/fixtures/wasm-src/src/app/og/route.js index 575c5a01ae..ba3552647f 100644 --- a/tests/fixtures/wasm-src/src/app/og/route.js +++ b/tests/fixtures/wasm-src/src/app/og/route.js @@ -1,4 +1,5 @@ -import { ImageResponse } from '@vercel/og' +// see next.config for details about 'next-og-alias' +import { ImageResponse } from 'next-og-alias' export async function GET() { return new ImageResponse(
hi
, { 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 index a693c6f5df..58ed5d86c5 100644 --- a/tests/fixtures/wasm-src/src/pages/api/og-wrong-runtime.js +++ b/tests/fixtures/wasm-src/src/pages/api/og-wrong-runtime.js @@ -1,5 +1,5 @@ -// /pages/api/og.jsx -import { ImageResponse } from '@vercel/og' +// see next.config for details about 'next-og-alias' +import { ImageResponse } from 'next-og-alias' export default function () { return new ImageResponse( diff --git a/tests/fixtures/wasm-src/src/pages/api/og.js b/tests/fixtures/wasm-src/src/pages/api/og.js index 55ab54d2c1..f3885a194e 100644 --- a/tests/fixtures/wasm-src/src/pages/api/og.js +++ b/tests/fixtures/wasm-src/src/pages/api/og.js @@ -1,5 +1,5 @@ -// /pages/api/og.jsx -import { ImageResponse } from '@vercel/og' +// see next.config for details about 'next-og-alias' +import { ImageResponse } from 'next-og-alias' export const config = { runtime: 'edge', diff --git a/tests/fixtures/wasm/app/og-node/route.js b/tests/fixtures/wasm/app/og-node/route.js index 6338e7e61b..2fa26189c2 100644 --- a/tests/fixtures/wasm/app/og-node/route.js +++ b/tests/fixtures/wasm/app/og-node/route.js @@ -1,4 +1,5 @@ -import { ImageResponse } from '@vercel/og' +// see next.config for details about 'next-og-alias' +import { ImageResponse } from 'next-og-alias' export async function GET() { return new ImageResponse(
hi
, { diff --git a/tests/fixtures/wasm/app/og/route.js b/tests/fixtures/wasm/app/og/route.js index 575c5a01ae..ba3552647f 100644 --- a/tests/fixtures/wasm/app/og/route.js +++ b/tests/fixtures/wasm/app/og/route.js @@ -1,4 +1,5 @@ -import { ImageResponse } from '@vercel/og' +// see next.config for details about 'next-og-alias' +import { ImageResponse } from 'next-og-alias' export async function GET() { return new ImageResponse(
hi
, { diff --git a/tests/fixtures/wasm/next.config.js b/tests/fixtures/wasm/next.config.js index 4263b7f9c2..8f60441cf4 100644 --- a/tests/fixtures/wasm/next.config.js +++ b/tests/fixtures/wasm/next.config.js @@ -1,5 +1,6 @@ const { platform } = require('process') const fsPromises = require('fs/promises') +const { satisfies } = require('semver') // 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 @@ -28,4 +29,28 @@ module.exports = { ignoreDuringBuilds: true, }, outputFileTracingRoot: __dirname, + outputFileTracingRoot: __dirname, + // there is no single way to use `next/og` or `@vercel/og` depending on Next.js version + // - next@<14 doesn't have 'next/og' export + // - next turbopack builds doesn't work with `@vercel/og` + // so this adds `next-og-alias` alias depending on next version for both webpack and turbopack + // so we can test this in all the versions + webpack: (config) => { + const hasNextOg = !satisfies(require('next/package.json').version, '<14.0.0', { + includePrerelease: true, + }) + + if (!hasNextOg) { + config.resolve.alias['next-og-alias$'] = '@vercel/og' + } else { + config.resolve.alias['next-og-alias$'] = 'next/og' + } + + return config + }, + turbopack: { + resolveAlias: { + 'next-og-alias': 'next/og', + }, + }, } diff --git a/tests/fixtures/wasm/package.json b/tests/fixtures/wasm/package.json index d5a533479f..7cbbd4d7ba 100644 --- a/tests/fixtures/wasm/package.json +++ b/tests/fixtures/wasm/package.json @@ -11,6 +11,7 @@ "@vercel/og": "latest", "next": "latest", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "semver": "^7.7.2" } } diff --git a/tests/fixtures/wasm/pages/api/og-wrong-runtime.js b/tests/fixtures/wasm/pages/api/og-wrong-runtime.js index a693c6f5df..58ed5d86c5 100644 --- a/tests/fixtures/wasm/pages/api/og-wrong-runtime.js +++ b/tests/fixtures/wasm/pages/api/og-wrong-runtime.js @@ -1,5 +1,5 @@ -// /pages/api/og.jsx -import { ImageResponse } from '@vercel/og' +// see next.config for details about 'next-og-alias' +import { ImageResponse } from 'next-og-alias' export default function () { return new ImageResponse( diff --git a/tests/fixtures/wasm/pages/api/og.js b/tests/fixtures/wasm/pages/api/og.js index 55ab54d2c1..f3885a194e 100644 --- a/tests/fixtures/wasm/pages/api/og.js +++ b/tests/fixtures/wasm/pages/api/og.js @@ -1,5 +1,5 @@ -// /pages/api/og.jsx -import { ImageResponse } from '@vercel/og' +// see next.config for details about 'next-og-alias' +import { ImageResponse } from 'next-og-alias' export const config = { runtime: 'edge', diff --git a/tests/integration/simple-app.test.ts b/tests/integration/simple-app.test.ts index d49dcc9e3d..7d865559fc 100644 --- a/tests/integration/simple-app.test.ts +++ b/tests/integration/simple-app.test.ts @@ -35,6 +35,7 @@ import { startMockBlobStore, } from '../utils/helpers.js' import { + hasDefaultTurbopackBuilds, nextVersionSatisfies, shouldHaveAppRouterGlobalErrorInPrerenderManifest, shouldHaveAppRouterNotFoundInPrerenderManifest, @@ -421,19 +422,23 @@ test.skipIf(process.env.NEXT_VERSION !== 'canary')( }, ) -test('can require CJS module that is not bundled', async (ctx) => { - await createFixture('simple', ctx) - await runPlugin(ctx) +// setup for this test only works with webpack builds due to usage of ` __non_webpack_require__` to avoid bundling a file +test.skipIf(hasDefaultTurbopackBuilds())( + 'can require CJS module that is not bundled', + async (ctx) => { + await createFixture('simple', ctx) + await runPlugin(ctx) - const response = await invokeFunction(ctx, { url: '/api/cjs-file-with-js-extension' }) + const response = await invokeFunction(ctx, { url: '/api/cjs-file-with-js-extension' }) - expect(response.statusCode).toBe(200) + expect(response.statusCode).toBe(200) - const parsedBody = JSON.parse(response.body) + const parsedBody = JSON.parse(response.body) - expect(parsedBody.notBundledCJSModule.isBundled).toEqual(false) - expect(parsedBody.bundledCJSModule.isBundled).toEqual(true) -}) + expect(parsedBody.notBundledCJSModule.isBundled).toEqual(false) + expect(parsedBody.bundledCJSModule.isBundled).toEqual(true) + }, +) describe('next patching', async () => { const { cp: originalCp, appendFile } = (await vi.importActual( diff --git a/tests/utils/create-e2e-fixture.ts b/tests/utils/create-e2e-fixture.ts index fe2546da2e..639a996aeb 100644 --- a/tests/utils/create-e2e-fixture.ts +++ b/tests/utils/create-e2e-fixture.ts @@ -69,7 +69,10 @@ export const createE2EFixture = async (fixture: string, config: E2EConfig = {}) } console.log('\n\n\n🪵 Deploy logs:') console.log(logs) - // on failures we don't delete the deploy + // on failures we don't delete the deploy, but we do cleanup the fixture from filesystem in CI + if (process.env.CI) { + return cleanup(isolatedFixtureRoot, undefined) + } } try { const [packageName] = await Promise.all([ @@ -392,14 +395,12 @@ export const fixtureFactories = { serverComponents: () => createE2EFixture('server-components'), nxIntegrated: () => createE2EFixture('nx-integrated', { - packageManger: 'pnpm', packagePath: 'apps/next-app', buildCommand: 'nx run next-app:build', publishDirectory: 'dist/apps/next-app/.next', }), nxIntegratedDistDir: () => createE2EFixture('nx-integrated', { - packageManger: 'pnpm', packagePath: 'apps/custom-dist-dir', buildCommand: 'nx run custom-dist-dir:build', publishDirectory: 'dist/apps/custom-dist-dir/dist', diff --git a/tests/utils/fixture.ts b/tests/utils/fixture.ts index d0f92bf530..8dfc6b83d7 100644 --- a/tests/utils/fixture.ts +++ b/tests/utils/fixture.ts @@ -31,7 +31,7 @@ import { } from '../../src/build/plugin-context.js' import { BLOB_TOKEN } from './constants.mjs' import { type FixtureTestContext } from './contexts.js' -import { setNextVersionInFixture } from './next-version-helpers.mjs' +import { hasDefaultTurbopackBuilds, setNextVersionInFixture } from './next-version-helpers.mjs' const bootstrapURL = await getBootstrapURL() const actualCwd = await vi.importActual('process').then((p) => p.cwd()) @@ -569,5 +569,8 @@ export async function invokeSandboxedFunction( } export const EDGE_MIDDLEWARE_FUNCTION_NAME = '___netlify-edge-handler-middleware' -export const EDGE_MIDDLEWARE_SRC_FUNCTION_NAME = '___netlify-edge-handler-src-middleware' +// Turbopack has different output than webpack +export const EDGE_MIDDLEWARE_SRC_FUNCTION_NAME = hasDefaultTurbopackBuilds() + ? EDGE_MIDDLEWARE_FUNCTION_NAME + : '___netlify-edge-handler-src-middleware' export const NODE_MIDDLEWARE_FUNCTION_NAME = '___netlify-edge-handler-node-middleware' diff --git a/tests/utils/next-version-helpers.mjs b/tests/utils/next-version-helpers.mjs index 018639ab49..5761f20ca6 100644 --- a/tests/utils/next-version-helpers.mjs +++ b/tests/utils/next-version-helpers.mjs @@ -49,6 +49,10 @@ export function hasNodeMiddlewareSupport() { return nextVersionSatisfies(isNextCanary() ? '>=15.2.0' : '>=15.5.0') } +export function hasDefaultTurbopackBuilds() { + return nextVersionSatisfies('>=15.6.0-canary.40') +} + /** * Check if current next version requires React 19 * @param {string} version Next version