diff --git a/docs/02-app/index.mdx b/docs/02-app/index.mdx index 9a53b0e040235..b188a536e9096 100644 --- a/docs/02-app/index.mdx +++ b/docs/02-app/index.mdx @@ -37,6 +37,7 @@ Here are some common authentication solutions that support the App Router: - [NextAuth.js](https://next-auth.js.org/configuration/nextjs#in-app-router) - [Clerk](https://clerk.com/docs/quickstarts/nextjs) +- [Stack Auth](https://docs.stack-auth.com/getting-started/setup) - [Lucia](https://lucia-auth.com/getting-started/nextjs-app) - [Auth0](https://github.com/auth0/nextjs-auth0#app-router) - [Stytch](https://stytch.com/docs/example-apps/frontend/nextjs) diff --git a/examples/cms-contentful/.env.local.example b/examples/cms-contentful/.env.local.example index eac50f166461e..4b0e3ac561ee6 100644 --- a/examples/cms-contentful/.env.local.example +++ b/examples/cms-contentful/.env.local.example @@ -1,5 +1,4 @@ CONTENTFUL_SPACE_ID= CONTENTFUL_ACCESS_TOKEN= CONTENTFUL_PREVIEW_ACCESS_TOKEN= -CONTENTFUL_PREVIEW_SECRET= CONTENTFUL_REVALIDATE_SECRET= \ No newline at end of file diff --git a/examples/cms-contentful/app/api/draft/route.ts b/examples/cms-contentful/app/api/draft/route.ts index fb0d83fb20838..a89c2f3cd13d6 100644 --- a/examples/cms-contentful/app/api/draft/route.ts +++ b/examples/cms-contentful/app/api/draft/route.ts @@ -1,22 +1,2 @@ -import { draftMode } from "next/headers"; -import { redirect } from "next/navigation"; -import { getPreviewPostBySlug } from "../../../lib/api"; - -export async function GET(request: Request) { - const { searchParams } = new URL(request.url); - const secret = searchParams.get("secret"); - const slug = searchParams.get("slug"); - - if (secret !== process.env.CONTENTFUL_PREVIEW_SECRET) { - return new Response("Invalid token", { status: 401 }); - } - - const post = await getPreviewPostBySlug(slug); - - if (!post) { - return new Response("Invalid slug", { status: 401 }); - } - - draftMode().enable(); - redirect(`/posts/${post.slug}`); -} +//@ts-ignore +export { enableDraftHandler as GET } from "@contentful/vercel-nextjs-toolkit/app-router"; diff --git a/examples/cms-contentful/package.json b/examples/cms-contentful/package.json index 6f8cbac311b23..232212205ed96 100644 --- a/examples/cms-contentful/package.json +++ b/examples/cms-contentful/package.json @@ -9,6 +9,7 @@ "dependencies": { "@contentful/rich-text-react-renderer": "^15.17.1", "@contentful/rich-text-types": "^16.2.1", + "@contentful/vercel-nextjs-toolkit": "latest", "@tailwindcss/typography": "0.5.9", "@types/node": "^20.5.0", "@types/react": "^18.2.20", diff --git a/examples/inngest/package.json b/examples/inngest/package.json index c86ec816af529..d5a93e5b197df 100755 --- a/examples/inngest/package.json +++ b/examples/inngest/package.json @@ -2,13 +2,13 @@ "private": true, "scripts": { "dev": "concurrently \"npm:dev:*\"", - "dev:next": "next dev", - "dev:inngest": "npx inngest-cli@latest dev", + "dev:next": "next dev --turbo", + "dev:inngest": "inngest-cli dev --no-discovery -u http://localhost:3000/api/inngest", "build": "next build", "start": "next start" }, "dependencies": { - "inngest": "latest", + "inngest": "3.x", "next": "latest", "react": "18.2.0", "react-dom": "18.2.0" @@ -19,6 +19,7 @@ "@types/react-dom": "18.2.7", "concurrently": "^8.2.1", "encoding": "^0.1.13", - "typescript": "5.2.2" + "inngest-cli": "latest", + "typescript": "5.5.4" } } diff --git a/examples/inngest/src/app/api/inngest/route.ts b/examples/inngest/src/app/api/inngest/route.ts index 1c4353142736f..6b08044ea5ce1 100755 --- a/examples/inngest/src/app/api/inngest/route.ts +++ b/examples/inngest/src/app/api/inngest/route.ts @@ -1,5 +1,4 @@ +import { inngestConfig } from "@/inngest/inngest.config"; import { serve } from "inngest/next"; -import { inngest } from "@/inngest/inngest.client"; -import { helloWorld } from "@/inngest/functions/hello-world"; -export const { GET, POST, PUT } = serve(inngest, [helloWorld]); +export const { GET, POST, PUT } = serve(inngestConfig); diff --git a/examples/inngest/src/app/page.tsx b/examples/inngest/src/app/page.tsx index 3089b9b43009b..46c1f5fae0fbe 100755 --- a/examples/inngest/src/app/page.tsx +++ b/examples/inngest/src/app/page.tsx @@ -1,4 +1,4 @@ -import { inngest } from "@/inngest/inngest.client"; +import { inngest } from "@/inngest/inngest.config"; import { redirect } from "next/navigation"; export default function Home() { diff --git a/examples/inngest/src/inngest/functions/hello-world.ts b/examples/inngest/src/inngest/functions/hello-world.ts deleted file mode 100755 index a31ddf4d00159..0000000000000 --- a/examples/inngest/src/inngest/functions/hello-world.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { inngest } from "../inngest.client"; - -export const helloWorld = inngest.createFunction( - { id: "hello-world", name: "Hello World" }, - { event: "test/hello.world" }, - async ({ event, step }) => { - await step.sleep("sleep for a second", "1s"); - return { event, body: event.data.message }; - }, -); diff --git a/examples/inngest/src/inngest/inngest.client.ts b/examples/inngest/src/inngest/inngest.client.ts deleted file mode 100755 index c11ec011e3140..0000000000000 --- a/examples/inngest/src/inngest/inngest.client.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Inngest } from "inngest"; - -// Create a client to send and receive events -export const inngest = new Inngest({ id: "Basic Inngest Application" }); diff --git a/examples/inngest/src/inngest/inngest.config.ts b/examples/inngest/src/inngest/inngest.config.ts new file mode 100644 index 0000000000000..74617accec9b7 --- /dev/null +++ b/examples/inngest/src/inngest/inngest.config.ts @@ -0,0 +1,33 @@ +import { EventSchemas, Inngest } from "inngest"; + +// TypeScript schema for the events +export type Events = { + "test/hello.world": { + name: "test/hello.world"; + data: { + message: string; + }; + }; +}; + +// Inngest client to send and receive events +export const inngest = new Inngest({ + id: "demo-app", + schemas: new EventSchemas().fromRecord(), +}); + +// a function to execute, typically in its own file +const helloWorld = inngest.createFunction( + { id: "hello-world", name: "Hello World" }, + { event: "test/hello.world" }, + async ({ event, step }) => { + await step.sleep("sleep for a second", "1s"); + return { event, body: event.data.message }; + }, +); + +// configuration for the Inngest api router +export const inngestConfig = { + client: inngest, + functions: [helloWorld], +}; diff --git a/test/e2e/app-dir/scss/compilation-and-prefixing/compilation-and-prefixing.test.ts b/test/e2e/app-dir/scss/compilation-and-prefixing/compilation-and-prefixing.test.ts index a65dc69772c68..b84370e0380e3 100644 --- a/test/e2e/app-dir/scss/compilation-and-prefixing/compilation-and-prefixing.test.ts +++ b/test/e2e/app-dir/scss/compilation-and-prefixing/compilation-and-prefixing.test.ts @@ -1,8 +1,6 @@ /* eslint-env jest */ import { nextTestSetup } from 'e2e-utils' -import { readdir, readFile } from 'fs-extra' -import { join } from 'path' const nextConfig = { productionBrowserSourceMaps: true, @@ -33,55 +31,194 @@ describe.each([ ;(isNextDev ? describe.skip : describe)('Production only', () => { describe('CSS Compilation and Prefixing', () => { it(`should've compiled and prefixed`, async () => { - const cssFolder = join(next.testDir, '.next/static/css') + const $ = await next.render$('/') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const cssSheet = $('link[rel="stylesheet"]') + expect(cssSheet.length).toBe(1) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect( - cssContent.replace(/\/\*.*?\*\//g, '').trim() - ).toMatchInlineSnapshot( - `".redText ::placeholder{color:red}.flex-parsing{flex:0 0 calc(50% - var(--vertical-gutter))}"` - ) + const stylesheetUrl = cssSheet.attr('href') + + const cssContent = await next + .fetch(stylesheetUrl) + .then((res) => res.text()) + const cssContentWithoutSourceMap = cssContent + .replace(/\/\*.*?\*\//g, '') + .trim() + + if (process.env.TURBOPACK) { + if (dependencies.sass) { + expect(cssContentWithoutSourceMap).toMatchInlineSnapshot(` + ".redText ::placeholder { + color: red; + } + + .flex-parsing { + flex: 0 0 calc(50% - var(--vertical-gutter)); + }" + `) + } else { + expect(cssContentWithoutSourceMap).toMatchInlineSnapshot(` + ".redText ::placeholder { + color: red; + } + + .flex-parsing { + flex: 0 0 calc(50% - var(--vertical-gutter)); + }" + `) + } + } else { + if (dependencies.sass) { + expect(cssContentWithoutSourceMap).toMatchInlineSnapshot( + `".redText ::placeholder{color:red}.flex-parsing{flex:0 0 calc(50% - var(--vertical-gutter))}"` + ) + } else { + expect(cssContentWithoutSourceMap).toMatchInlineSnapshot( + `".redText ::placeholder{color:red}.flex-parsing{flex:0 0 calc(50% - var(--vertical-gutter))}"` + ) + } + } // Contains a source map - console.log({ cssContent }) expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) - }) - it(`should've emitted a source map`, async () => { - const cssFolder = join(next.testDir, '.next/static/css') + // Check sourcemap + const sourceMapUrl = /\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//.exec( + cssContent + )[1] - const files = await readdir(cssFolder) - const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f)) + const actualSourceMapUrl = stylesheetUrl.replace(/[^/]+$/, sourceMapUrl) + const sourceMapContent = await next + .fetch(actualSourceMapUrl) + .then((res) => res.text()) + const sourceMapContentParsed = JSON.parse(sourceMapContent) + // Ensure it doesn't have a specific path in the snapshot. + delete sourceMapContentParsed.file + delete sourceMapContentParsed.sources - expect(cssMapFiles.length).toBe(1) - const cssMapContent = ( - await readFile(join(cssFolder, cssMapFiles[0]), 'utf8') - ).trim() + if (process.env.TURBOPACK) { + if (dependencies.sass) { + expect(sourceMapContentParsed).toMatchInlineSnapshot(` + { + "sections": [ + { + "map": { + "mappings": "AAAA;;;;AAAiC", + "names": [], + "sources": [ + "turbopack://[project]/styles/global.scss.css", + ], + "sourcesContent": [ + ".redText ::placeholder{color:red}.flex-parsing{flex:0 0 calc(50% - var(--vertical-gutter))}", + ], + "version": 3, + }, + "offset": { + "column": 0, + "line": 1, + }, + }, + { + "map": { + "mappings": "A", + "names": [], + "sources": [], + "version": 3, + }, + "offset": { + "column": 0, + "line": 8, + }, + }, + ], + "version": 3, + } + `) + } else { + expect(sourceMapContentParsed).toMatchInlineSnapshot(` + { + "sections": [ + { + "map": { + "mappings": "AAAA;;;;AAAiC", + "names": [], + "sources": [ + "turbopack://[project]/styles/global.scss.css", + ], + "sourcesContent": [ + ".redText ::placeholder{color:red}.flex-parsing{flex:0 0 calc(50% - var(--vertical-gutter))}", + ], + "version": 3, + }, + "offset": { + "column": 0, + "line": 1, + }, + }, + { + "map": { + "mappings": "A", + "names": [], + "sources": [], + "version": 3, + }, + "offset": { + "column": 0, + "line": 8, + }, + }, + ], + "version": 3, + } + `) + } + } else { + if (dependencies.sass) { + expect(sourceMapContentParsed).toMatchInlineSnapshot(` + { + "mappings": "AAEE,uBACE,SAHE,CAON,cACE,2CAAA", + "names": [], + "sourceRoot": "", + "sourcesContent": [ + "$var: red; + .redText { + ::placeholder { + color: $var; + } + } - const { version, mappings, sourcesContent } = JSON.parse(cssMapContent) - expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(` - { - "mappings": "AAEE,uBACE,SAHE,CAON,cACE,2CAAA", - "sourcesContent": [ - "$var: red; - .redText { - ::placeholder { - color: $var; - } - } - - .flex-parsing { - flex: 0 0 calc(50% - var(--vertical-gutter)); - } - ", - ], - "version": 3, - } - `) + .flex-parsing { + flex: 0 0 calc(50% - var(--vertical-gutter)); + } + ", + ], + "version": 3, + } + `) + } else { + expect(sourceMapContentParsed).toMatchInlineSnapshot(` + { + "mappings": "AAEE,uBACE,SAHE,CAON,cACE,2CAAA", + "names": [], + "sourceRoot": "", + "sourcesContent": [ + "$var: red; + .redText { + ::placeholder { + color: $var; + } + } + + .flex-parsing { + flex: 0 0 calc(50% - var(--vertical-gutter)); + } + ", + ], + "version": 3, + } + `) + } + } }) }) }) diff --git a/test/lib/next-test-utils.ts b/test/lib/next-test-utils.ts index 9aac68044d560..47ae46446648c 100644 --- a/test/lib/next-test-utils.ts +++ b/test/lib/next-test-utils.ts @@ -875,7 +875,7 @@ export async function assertNoRedbox(browser: BrowserInterface) { `description: ${redboxDescription}\n` + `source: ${redboxSource}` ) - Error.captureStackTrace(error, assertHasRedbox) + Error.captureStackTrace(error, assertNoRedbox) throw error } } diff --git a/test/turbopack-build-tests-manifest.json b/test/turbopack-build-tests-manifest.json index fde602eb9ab4c..af7aafd3dc100 100644 --- a/test/turbopack-build-tests-manifest.json +++ b/test/turbopack-build-tests-manifest.json @@ -3623,13 +3623,11 @@ "runtimeError": false }, "test/e2e/app-dir/scss/compilation-and-prefixing/compilation-and-prefixing.test.ts": { - "passed": [], - "failed": [ + "passed": [ "SCSS Support ({\"sass\": \"1.54.0\"}) Production only CSS Compilation and Prefixing should've compiled and prefixed", - "SCSS Support ({\"sass\": \"1.54.0\"}) Production only CSS Compilation and Prefixing should've emitted a source map", - "SCSS Support ({\"sass-embedded\": \"1.75.0\"}) Production only CSS Compilation and Prefixing should've compiled and prefixed", - "SCSS Support ({\"sass-embedded\": \"1.75.0\"}) Production only CSS Compilation and Prefixing should've emitted a source map" + "SCSS Support ({\"sass-embedded\": \"1.75.0\"}) Production only CSS Compilation and Prefixing should've compiled and prefixed" ], + "failed": [], "pending": [], "flakey": [], "runtimeError": false @@ -5832,6 +5830,21 @@ "flakey": [], "runtimeError": false }, + "test/e2e/on-request-error/isr/isr.test.ts": { + "passed": [ + "on-request-error - isr app router ISR should capture correct reason for build errored route", + "on-request-error - isr app router ISR should capture correct reason for on-demand revalidated page", + "on-request-error - isr app router ISR should capture correct reason for on-demand revalidated route", + "on-request-error - isr app router ISR should capture correct reason for stale errored page", + "on-request-error - isr app router ISR should capture revalidate from server action", + "on-request-error - isr pages router ISR should capture correct reason for on-demand revalidated page", + "on-request-error - isr pages router ISR should capture correct reason for stale errored page" + ], + "failed": [], + "pending": [], + "flakey": [], + "runtimeError": false + }, "test/e2e/on-request-error/server-action-error/server-action-error.test.ts": { "passed": [ "on-request-error - server-action-error should catch server action error in listener callback in edge runtime", @@ -7845,20 +7858,20 @@ }, "test/integration/css/test/css-compilation.test.js": { "passed": [ - "CSS Support production mode Has CSS in computed styles in Production useLightnincsss(false) should have CSS for page", - "CSS Support production mode Has CSS in computed styles in Production useLightnincsss(false) should've preloaded the CSS file and injected it in ", - "CSS Support production mode Has CSS in computed styles in Production useLightnincsss(true) should have CSS for page", - "CSS Support production mode Has CSS in computed styles in Production useLightnincsss(true) should've preloaded the CSS file and injected it in ", - "CSS Support production mode React Lifecyce Order (production) useLightnincsss(false) should have the correct color on mount after navigation", - "CSS Support production mode React Lifecyce Order (production) useLightnincsss(true) should have the correct color on mount after navigation", - "CSS Support production mode Good CSS Import from node_modules useLightnincsss(false) should've emitted a single CSS file", - "CSS Support production mode Good CSS Import from node_modules useLightnincsss(true) should've emitted a single CSS file", "CSS Property Ordering production mode useLightnincsss(false) should have the border width (property ordering)", "CSS Property Ordering production mode useLightnincsss(true) should have the border width (property ordering)", "CSS Support production mode CSS Compilation and Prefixing useLightnincsss(false) should've compiled and prefixed", "CSS Support production mode CSS Compilation and Prefixing useLightnincsss(true) should've compiled and prefixed", + "CSS Support production mode Good CSS Import from node_modules useLightnincsss(false) should've emitted a single CSS file", + "CSS Support production mode Good CSS Import from node_modules useLightnincsss(true) should've emitted a single CSS file", "CSS Support production mode Good Nested CSS Import from node_modules useLightnincsss(false) should've emitted a single CSS file", - "CSS Support production mode Good Nested CSS Import from node_modules useLightnincsss(true) should've emitted a single CSS file" + "CSS Support production mode Good Nested CSS Import from node_modules useLightnincsss(true) should've emitted a single CSS file", + "CSS Support production mode Has CSS in computed styles in Production useLightnincsss(false) should have CSS for page", + "CSS Support production mode Has CSS in computed styles in Production useLightnincsss(false) should've preloaded the CSS file and injected it in ", + "CSS Support production mode Has CSS in computed styles in Production useLightnincsss(true) should have CSS for page", + "CSS Support production mode Has CSS in computed styles in Production useLightnincsss(true) should've preloaded the CSS file and injected it in ", + "CSS Support production mode React Lifecyce Order (production) useLightnincsss(false) should have the correct color on mount after navigation", + "CSS Support production mode React Lifecyce Order (production) useLightnincsss(true) should have the correct color on mount after navigation" ], "failed": [], "pending": [], @@ -7923,12 +7936,12 @@ }, "test/integration/css/test/css-rendering.test.js": { "passed": [ + "CSS Support CSS page transition inject