diff --git a/docs/02-app/02-api-reference/04-functions/cookies.mdx b/docs/02-app/02-api-reference/04-functions/cookies.mdx index ff3b09909a362..48909830ca921 100644 --- a/docs/02-app/02-api-reference/04-functions/cookies.mdx +++ b/docs/02-app/02-api-reference/04-functions/cookies.mdx @@ -52,8 +52,8 @@ A method that takes a cookie name and returns a `boolean` based on if the cookie import { cookies } from 'next/headers' export default function Page() { - const cookiesList = cookies() - const hasCookie = cookiesList.has('theme') + const cookieStore = cookies() + const hasCookie = cookieStore.has('theme') return '...' } ``` diff --git a/lerna.json b/lerna.json index d2ca4a1e23b69..ce098157e85c0 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "14.2.0-canary.28" + "version": "14.2.0-canary.29" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 39c243d0558a7..64313304f6075 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 6f4c0df9c9ca6..511df55e0d58c 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "description": "ESLint configuration used by Next.js.", "main": "index.js", "license": "MIT", @@ -10,9 +10,9 @@ }, "homepage": "https://nextjs.org/docs/app/building-your-application/configuring/eslint#eslint-config", "dependencies": { - "@next/eslint-plugin-next": "14.2.0-canary.28", + "@next/eslint-plugin-next": "14.2.0-canary.29", "@rushstack/eslint-patch": "^1.3.3", - "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.1", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.28.1", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index fe7f8ac555dd6..1cfad2cccbc2f 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -1,6 +1,6 @@ { "name": "@next/eslint-plugin-next", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "description": "ESLint plugin for Next.js.", "main": "dist/index.js", "license": "MIT", diff --git a/packages/font/package.json b/packages/font/package.json index 3122e2bc8c630..920837c2e2f3a 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,6 +1,6 @@ { "name": "@next/font", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "repository": { "url": "vercel/next.js", "directory": "packages/font" diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index ef1bc3e88a073..878b37e95790c 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 8aedd661fc29a..8984329d2794a 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index c963e593bdf34..51f13ea3e7fa3 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 871f8ce93fa0c..54687731d0c08 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index e4c5f91ed643d..f801b3bd442e2 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index 18e09e459f637..20c0687ebac17 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index b626f387b3457..f0847b35f6b42 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-swc/crates/next-api/src/app.rs b/packages/next-swc/crates/next-api/src/app.rs index 8bafad0c6d6e6..096879ef26f32 100644 --- a/packages/next-swc/crates/next-api/src/app.rs +++ b/packages/next-swc/crates/next-api/src/app.rs @@ -495,6 +495,7 @@ impl AppEndpoint { Vc::upcast(FileSource::new(path)), self.page.clone(), self.app_project.project().project_path(), + None, ) } diff --git a/packages/next-swc/crates/next-core/src/next_app/app_favicon_entry.rs b/packages/next-swc/crates/next-core/src/next_app/app_favicon_entry.rs index b72fc9af399ab..4d7d54dabb2e6 100644 --- a/packages/next-swc/crates/next-core/src/next_app/app_favicon_entry.rs +++ b/packages/next-swc/crates/next-core/src/next_app/app_favicon_entry.rs @@ -89,5 +89,6 @@ pub async fn get_app_route_favicon_entry( // TODO(alexkirsz) Get this from the metadata? AppPage(vec![PageSegment::Static("/favicon.ico".to_string())]), project_root, + None, )) } diff --git a/packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs b/packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs index 5cf997000e6dd..47eaf58d9bed8 100644 --- a/packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs +++ b/packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs @@ -16,6 +16,7 @@ use turbopack_binding::{ }; use crate::{ + app_segment_config::NextSegmentConfig, next_app::{AppEntry, AppPage, AppPath}, next_edge::entry::wrap_edge_entry, parse_segment_config_from_source, @@ -23,6 +24,12 @@ use crate::{ }; /// Computes the entry for a Next.js app route. +/// # Arguments +/// +/// * `original_segment_config` - A next segment config to be specified +/// explicitly for the given source. +/// For some cases `source` may not be the original but the handler (dynamic +/// metadata) which will lose segment config. #[turbo_tasks::function] pub async fn get_app_route_entry( nodejs_context: Vc, @@ -30,8 +37,10 @@ pub async fn get_app_route_entry( source: Vc>, page: AppPage, project_root: Vc, + original_segment_config: Option>, ) -> Result> { - let config = parse_segment_config_from_source(source); + let config = + original_segment_config.unwrap_or_else(|| parse_segment_config_from_source(source)); let is_edge = matches!(config.await?.runtime, Some(NextRuntime::Edge)); let context = if is_edge { edge_context diff --git a/packages/next-swc/crates/next-core/src/next_app/metadata/route.rs b/packages/next-swc/crates/next-core/src/next_app/metadata/route.rs index e7db3bc5a4a4d..363a9449a9535 100644 --- a/packages/next-swc/crates/next-core/src/next_app/metadata/route.rs +++ b/packages/next-swc/crates/next-core/src/next_app/metadata/route.rs @@ -9,7 +9,10 @@ use turbo_tasks::{ValueToString, Vc}; use turbopack_binding::{ turbo::tasks_fs::{File, FileContent, FileSystemPath}, turbopack::{ - core::{asset::AssetContent, source::Source, virtual_source::VirtualSource}, + core::{ + asset::AssetContent, file_source::FileSource, source::Source, + virtual_source::VirtualSource, + }, ecmascript::utils::StringifyJs, turbopack::ModuleAssetContext, }, @@ -20,6 +23,7 @@ use crate::{ app_structure::MetadataItem, mode::NextMode, next_app::{app_entry::AppEntry, app_route_entry::get_app_route_entry, AppPage, PageSegment}, + parse_segment_config_from_source, }; /// Computes the route source for a Next.js metadata file. @@ -55,12 +59,22 @@ pub fn get_app_metadata_route_entry( mode: NextMode, metadata: MetadataItem, ) -> Vc { + // Read original source's segment config before replacing source into + // dynamic|static metadata route handler. + let original_path = match metadata { + MetadataItem::Static { path } | MetadataItem::Dynamic { path } => path, + }; + + let source = Vc::upcast(FileSource::new(original_path)); + let config = parse_segment_config_from_source(source); + get_app_route_entry( nodejs_context, edge_context, get_app_metadata_route_source(page.clone(), mode, metadata), page, project_root, + Some(config), ) } diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index 50242f06bc681..3bdd824f6381b 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "private": true, "scripts": { "clean": "node ../../scripts/rm.mjs native", diff --git a/packages/next/package.json b/packages/next/package.json index 677e59b023dfe..16a1e05af4380 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -92,7 +92,7 @@ ] }, "dependencies": { - "@next/env": "14.2.0-canary.28", + "@next/env": "14.2.0-canary.29", "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -145,10 +145,10 @@ "@jest/types": "29.5.0", "@mswjs/interceptors": "0.23.0", "@napi-rs/triples": "1.2.0", - "@next/polyfill-module": "14.2.0-canary.28", - "@next/polyfill-nomodule": "14.2.0-canary.28", - "@next/react-refresh-utils": "14.2.0-canary.28", - "@next/swc": "14.2.0-canary.28", + "@next/polyfill-module": "14.2.0-canary.29", + "@next/polyfill-nomodule": "14.2.0-canary.29", + "@next/react-refresh-utils": "14.2.0-canary.29", + "@next/swc": "14.2.0-canary.29", "@opentelemetry/api": "1.6.0", "@playwright/test": "1.41.2", "@taskr/clear": "1.1.0", diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index eb287a13f4900..64e12f37b78ba 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-refresh-utils", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", diff --git a/packages/third-parties/package.json b/packages/third-parties/package.json index c7801544e1e0d..0ef20d589ea90 100644 --- a/packages/third-parties/package.json +++ b/packages/third-parties/package.json @@ -1,6 +1,6 @@ { "name": "@next/third-parties", - "version": "14.2.0-canary.28", + "version": "14.2.0-canary.29", "repository": { "url": "vercel/next.js", "directory": "packages/third-parties" @@ -26,7 +26,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "14.2.0-canary.28", + "next": "14.2.0-canary.29", "outdent": "0.8.0", "prettier": "2.5.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ebcf16148a0cd..df8778b4c77c0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -747,13 +747,13 @@ importers: packages/eslint-config-next: dependencies: '@next/eslint-plugin-next': - specifier: 14.2.0-canary.28 + specifier: 14.2.0-canary.29 version: link:../eslint-plugin-next '@rushstack/eslint-patch': specifier: ^1.3.3 version: 1.3.3 '@typescript-eslint/parser': - specifier: ^5.4.2 || ^6.0.0 || ^7.0.1 + specifier: ^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0 version: 6.14.0(eslint@8.31.0)(typescript@4.8.2) eslint: specifier: ^7.23.0 || ^8.0.0 @@ -809,7 +809,7 @@ importers: packages/next: dependencies: '@next/env': - specifier: 14.2.0-canary.28 + specifier: 14.2.0-canary.29 version: link:../next-env '@swc/helpers': specifier: 0.5.5 @@ -930,16 +930,16 @@ importers: specifier: 1.2.0 version: 1.2.0 '@next/polyfill-module': - specifier: 14.2.0-canary.28 + specifier: 14.2.0-canary.29 version: link:../next-polyfill-module '@next/polyfill-nomodule': - specifier: 14.2.0-canary.28 + specifier: 14.2.0-canary.29 version: link:../next-polyfill-nomodule '@next/react-refresh-utils': - specifier: 14.2.0-canary.28 + specifier: 14.2.0-canary.29 version: link:../react-refresh-utils '@next/swc': - specifier: 14.2.0-canary.28 + specifier: 14.2.0-canary.29 version: link:../next-swc '@opentelemetry/api': specifier: 1.6.0 @@ -1554,7 +1554,7 @@ importers: version: 1.0.20 devDependencies: next: - specifier: 14.2.0-canary.28 + specifier: 14.2.0-canary.29 version: link:../next outdent: specifier: 0.8.0 @@ -7459,7 +7459,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: '@typescript-eslint/types': 5.62.0 - eslint-visitor-keys: 3.4.1 + eslint-visitor-keys: 3.4.3 dev: true /@typescript-eslint/visitor-keys@6.14.0: @@ -7467,7 +7467,7 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dependencies: '@typescript-eslint/types': 6.14.0 - eslint-visitor-keys: 3.4.1 + eslint-visitor-keys: 3.4.3 /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} @@ -11892,11 +11892,11 @@ packages: /eslint-visitor-keys@3.4.1: resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: false /eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true /eslint@7.24.0: resolution: {integrity: sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ==} @@ -13981,7 +13981,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.1.1 transitivePeerDependencies: - supports-color dev: true diff --git a/test/development/acceptance/__snapshots__/error-recovery.test.ts.snap b/test/development/acceptance/__snapshots__/error-recovery.test.ts.snap index 8c12e2cd48138..ec3621003e019 100644 --- a/test/development/acceptance/__snapshots__/error-recovery.test.ts.snap +++ b/test/development/acceptance/__snapshots__/error-recovery.test.ts.snap @@ -11,3 +11,15 @@ exports[`ReactRefreshLogBox default syntax > runtime error 1`] = ` 7 | export default function FunctionNamed() { 8 | return
" `; + +exports[`ReactRefreshLogBox turbo syntax > runtime error 1`] = ` +"index.js (5:9) @ eval + + 3 | setInterval(() => { + 4 | i++ +> 5 | throw Error('no ' + i) + | ^ + 6 | }, 1000) + 7 | export default function FunctionNamed() { + 8 | return
" +`; diff --git a/test/development/acceptance/error-recovery.test.ts b/test/development/acceptance/error-recovery.test.ts index 0b9e4c61216a2..1aaf2cb034d1a 100644 --- a/test/development/acceptance/error-recovery.test.ts +++ b/test/development/acceptance/error-recovery.test.ts @@ -434,50 +434,81 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox %s', () => { await new Promise((resolve) => setTimeout(resolve, 1000)) expect(await session.hasRedbox()).toBe(true) - expect(next.normalizeTestDirContent(await session.getRedboxSource())) - .toMatchInlineSnapshot(` - "./index.js - Error: - x Expected '}', got '' - ,-[TEST_DIR/index.js:4:1] - 4 | i++ - 5 | throw Error('no ' + i) - 6 | }, 1000) - 7 | export default function FunctionNamed() { - : ^ - \`---- - - Caused by: - Syntax Error - - Import trace for requested module: - ./index.js - ./pages/index.js" - `) + let redboxSource = next.normalizeTestDirContent( + await session.getRedboxSource() + ) + + if (isTurbopack) { + // TODO: Remove this branching once import traces are implemented in Turbopack + expect(redboxSource).toMatchInlineSnapshot(` + "./index.js:7:41 + Parsing ecmascript source code failed + 5 | throw Error('no ' + i) + 6 | }, 1000) + > 7 | export default function FunctionNamed() { + | ^ + + Expected '}', got ''" + `) + } else { + expect(redboxSource).toMatchInlineSnapshot(` + "./index.js + Error: + x Expected '}', got '' + ,-[TEST_DIR/index.js:4:1] + 4 | i++ + 5 | throw Error('no ' + i) + 6 | }, 1000) + 7 | export default function FunctionNamed() { + : ^ + \`---- + + Caused by: + Syntax Error + + Import trace for requested module: + ./index.js + ./pages/index.js" + `) + } // Test that runtime error does not take over: await new Promise((resolve) => setTimeout(resolve, 2000)) expect(await session.hasRedbox()).toBe(true) - expect(next.normalizeTestDirContent(await session.getRedboxSource())) - .toMatchInlineSnapshot(` - "./index.js - Error: - x Expected '}', got '' - ,-[TEST_DIR/index.js:4:1] - 4 | i++ - 5 | throw Error('no ' + i) - 6 | }, 1000) - 7 | export default function FunctionNamed() { - : ^ - \`---- - - Caused by: - Syntax Error - - Import trace for requested module: - ./index.js - ./pages/index.js" - `) + redboxSource = next.normalizeTestDirContent(await session.getRedboxSource()) + if (isTurbopack) { + // TODO: Remove this branching once import traces are implemented in Turbopack + expect(redboxSource).toMatchInlineSnapshot(` + "./index.js:7:41 + Parsing ecmascript source code failed + 5 | throw Error('no ' + i) + 6 | }, 1000) + > 7 | export default function FunctionNamed() { + | ^ + + Expected '}', got ''" + `) + } else { + expect(redboxSource).toMatchInlineSnapshot(` + "./index.js + Error: + x Expected '}', got '' + ,-[TEST_DIR/index.js:4:1] + 4 | i++ + 5 | throw Error('no ' + i) + 6 | }, 1000) + 7 | export default function FunctionNamed() { + : ^ + \`---- + + Caused by: + Syntax Error + + Import trace for requested module: + ./index.js + ./pages/index.js" + `) + } await cleanup() }) diff --git a/test/turbopack-tests-manifest.json b/test/turbopack-tests-manifest.json index d319ba79fe102..b3bbb9ce3e9d9 100644 --- a/test/turbopack-tests-manifest.json +++ b/test/turbopack-tests-manifest.json @@ -3774,11 +3774,10 @@ "app dir - metadata dynamic routes social image routes should support params as argument in dynamic routes", "app dir - metadata dynamic routes text routes should handle robots.[ext] dynamic routes", "app dir - metadata dynamic routes text routes should handle sitemap.[ext] dynamic routes", - "app dir - metadata dynamic routes text routes should not throw if client components are imported but not used" - ], - "failed": [ + "app dir - metadata dynamic routes text routes should not throw if client components are imported but not used", "app dir - metadata dynamic routes social image routes should handle custom fonts in both edge and nodejs runtime" ], + "failed": [], "pending": [], "flakey": [], "runtimeError": false