diff --git a/.github/next-stats-action.Dockerfile b/.github/next-stats-action.Dockerfile index 8a0b3304f2e4..214095a8c725 100644 --- a/.github/next-stats-action.Dockerfile +++ b/.github/next-stats-action.Dockerfile @@ -16,7 +16,7 @@ RUN corepack enable FROM base AS pnpm-deploy WORKDIR /dot-github -COPY pnpm-lock.yaml pnpm-workspace.yaml . +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY --exclude=actions/*/node_modules \ actions/next-stats-action actions/next-stats-action RUN pnpm deploy --filter=next-stats-action --production /next-stats diff --git a/.github/package.json b/.github/package.json new file mode 100644 index 000000000000..a59d9a3884b2 --- /dev/null +++ b/.github/package.json @@ -0,0 +1,5 @@ +{ + "name": "next-github-workflows", + "private": true, + "packageManager": "pnpm@10.33.0" +} diff --git a/.github/pnpm-lock.yaml b/.github/pnpm-lock.yaml index 91800d35ee90..004c92e24e2e 100644 --- a/.github/pnpm-lock.yaml +++ b/.github/pnpm-lock.yaml @@ -7,6 +7,8 @@ settings: importers: + .: {} + actions/needs-triage: dependencies: '@actions/core': diff --git a/crates/next-api/build.rs b/crates/next-api/build.rs index 65d1aed8ba3b..1ea5e13aa508 100644 --- a/crates/next-api/build.rs +++ b/crates/next-api/build.rs @@ -6,8 +6,8 @@ fn main() -> anyhow::Result<()> { .target_triple(true) .build()?; vergen::Emitter::default() - .add_instructions(&cargo)? .fail_on_error() + .add_instructions(&cargo)? .emit()?; Ok(()) diff --git a/crates/next-core/src/app_page_loader_tree.rs b/crates/next-core/src/app_page_loader_tree.rs index 24f1d202ab85..746aeec2f6b2 100644 --- a/crates/next-core/src/app_page_loader_tree.rs +++ b/crates/next-core/src/app_page_loader_tree.rs @@ -50,11 +50,12 @@ impl AppPageLoaderTreeBuilder { &mut self, module_type: AppDirModuleType, path: Option, + depth: u32, ) -> Result<()> { if let Some(path) = path { let tuple_code = self .base - .create_module_tuple_code(module_type, path) + .create_module_tuple_code(module_type, path, depth) .await?; writeln!( @@ -71,6 +72,7 @@ impl AppPageLoaderTreeBuilder { app_page: &AppPage, metadata: &Metadata, global_metadata: Option<&GlobalMetadata>, + depth: u32, ) -> Result<()> { if metadata.is_empty() && global_metadata @@ -107,13 +109,13 @@ impl AppPageLoaderTreeBuilder { icon.clone() }; - self.write_metadata_items(app_page, "icon", icon.iter()) + self.write_metadata_items(app_page, "icon", icon.iter(), depth) .await?; - self.write_metadata_items(app_page, "apple", apple.iter()) + self.write_metadata_items(app_page, "apple", apple.iter(), depth) .await?; - self.write_metadata_items(app_page, "twitter", twitter.iter()) + self.write_metadata_items(app_page, "twitter", twitter.iter(), depth) .await?; - self.write_metadata_items(app_page, "openGraph", open_graph.iter()) + self.write_metadata_items(app_page, "openGraph", open_graph.iter(), depth) .await?; if let Some(global_metadata) = global_metadata { @@ -151,6 +153,7 @@ impl AppPageLoaderTreeBuilder { app_page: &AppPage, name: &str, it: impl Iterator, + depth: u32, ) -> Result<()> { let mut it = it.peekable(); if it.peek().is_none() { @@ -158,7 +161,8 @@ impl AppPageLoaderTreeBuilder { } writeln!(self.loader_tree_code, " {name}: [")?; for item in it { - self.write_metadata_item(app_page, name, item).await?; + self.write_metadata_item(app_page, name, item, depth) + .await?; } writeln!(self.loader_tree_code, " ],")?; Ok(()) @@ -169,6 +173,7 @@ impl AppPageLoaderTreeBuilder { app_page: &AppPage, name: &str, item: &MetadataWithAltItem, + depth: u32, ) -> Result<()> { match item { MetadataWithAltItem::Static { path, alt_path } => { @@ -178,6 +183,7 @@ impl AppPageLoaderTreeBuilder { item, path.clone(), alt_path.clone(), + depth, ) .await?; } @@ -189,13 +195,14 @@ impl AppPageLoaderTreeBuilder { // This should use the same importing mechanism as create_module_tuple_code, so that // the relative order of items is retained (which isn't the case // when mixing ESM imports and requires). - self.base.imports.push( + self.base.imports.push(( + depth, format!( "const {identifier} = () => require(/*turbopackChunkingType: \ shared*/\"{inner_module_id}\");" ) .into(), - ); + )); let source = dynamic_image_metadata_source( *ResolvedVc::upcast(self.base.module_asset_context), @@ -226,6 +233,7 @@ impl AppPageLoaderTreeBuilder { item: &MetadataWithAltItem, path: FileSystemPath, alt_path: Option, + depth: u32, ) -> Result<()> { let i = self.base.unique_number(); @@ -235,13 +243,14 @@ impl AppPageLoaderTreeBuilder { // This should use the same importing mechanism as create_module_tuple_code, so that the // relative order of items is retained (which isn't the case when mixing ESM imports and // requires). - self.base.imports.push( + self.base.imports.push(( + depth, format!( "const {identifier} = () => require(/*turbopackChunkingType: \ shared*/\"{inner_module_id}\");" ) .into(), - ); + )); let module = StructuredImageModuleType::create_module( Vc::upcast(FileSource::new(path.clone())), BlurPlaceholderMode::None, @@ -259,13 +268,14 @@ impl AppPageLoaderTreeBuilder { // This should use the same importing mechanism as create_module_tuple_code, so that the // relative order of items is retained (which isn't the case when mixing ESM imports and // requires). - self.base.imports.push( + self.base.imports.push(( + depth, format!( "const {identifier} = () => require(/*turbopackChunkingType: \ shared*/\"{inner_module_id}\");" ) .into(), - ); + )); let module = self .base @@ -342,7 +352,12 @@ impl AppPageLoaderTreeBuilder { Ok(()) } - async fn walk_tree(&mut self, loader_tree: &AppPageLoaderTree, root: bool) -> Result<()> { + async fn walk_tree( + &mut self, + loader_tree: &AppPageLoaderTree, + root: bool, + depth: u32, + ) -> Result<()> { use std::fmt::Write; let AppPageLoaderTree { @@ -385,38 +400,48 @@ impl AppPageLoaderTreeBuilder { app_page, metadata, if root { Some(global_metadata) } else { None }, + depth, ) .await?; - self.write_modules_entry(AppDirModuleType::Layout, layout.clone()) + self.write_modules_entry(AppDirModuleType::Layout, layout.clone(), depth) .await?; - self.write_modules_entry(AppDirModuleType::Error, error.clone()) + self.write_modules_entry(AppDirModuleType::Error, error.clone(), depth) .await?; - self.write_modules_entry(AppDirModuleType::Loading, loading.clone()) + self.write_modules_entry(AppDirModuleType::Loading, loading.clone(), depth) .await?; - self.write_modules_entry(AppDirModuleType::Template, template.clone()) + self.write_modules_entry(AppDirModuleType::Template, template.clone(), depth) .await?; - self.write_modules_entry(AppDirModuleType::NotFound, not_found.clone()) + self.write_modules_entry(AppDirModuleType::NotFound, not_found.clone(), depth) .await?; - self.write_modules_entry(AppDirModuleType::Forbidden, forbidden.clone()) + self.write_modules_entry(AppDirModuleType::Forbidden, forbidden.clone(), depth) .await?; - self.write_modules_entry(AppDirModuleType::Unauthorized, unauthorized.clone()) + self.write_modules_entry(AppDirModuleType::Unauthorized, unauthorized.clone(), depth) .await?; - self.write_modules_entry(AppDirModuleType::Page, page.clone()) + self.write_modules_entry(AppDirModuleType::Page, page.clone(), depth) .await?; - self.write_modules_entry(AppDirModuleType::DefaultPage, default.clone()) + self.write_modules_entry(AppDirModuleType::DefaultPage, default.clone(), depth) .await?; - self.write_modules_entry(AppDirModuleType::GlobalError, global_error.clone()) - .await?; - self.write_modules_entry(AppDirModuleType::GlobalNotFound, global_not_found.clone()) + self.write_modules_entry(AppDirModuleType::GlobalError, global_error.clone(), depth) .await?; + self.write_modules_entry( + AppDirModuleType::GlobalNotFound, + global_not_found.clone(), + depth, + ) + .await?; let modules_code = replace(&mut self.loader_tree_code, temp_loader_tree_code); // add parallel_routes for (key, parallel_route) in parallel_routes.iter() { write!(self.loader_tree_code, "{key}: ", key = StringifyJs(key))?; - Box::pin(self.walk_tree(parallel_route, false)).await?; + let next_depth = if key.as_str() == "children" { + depth + 1 + } else { + depth + }; + Box::pin(self.walk_tree(parallel_route, false, next_depth)).await?; writeln!(self.loader_tree_code, ",")?; } writeln!(self.loader_tree_code, "}}, {{")?; @@ -454,9 +479,11 @@ impl AppPageLoaderTreeBuilder { .insert(GLOBAL_NOT_FOUND.into(), module); }; - self.walk_tree(loader_tree, true).await?; + self.walk_tree(loader_tree, true, 0).await?; + let mut imports = self.base.imports; + imports.sort_by_key(|(position, _)| *position); Ok(AppPageLoaderTreeModule { - imports: self.base.imports, + imports: imports.into_iter().map(|(_, import)| import).collect(), loader_tree_code: self.loader_tree_code.into(), inner_assets: self.base.inner_assets, }) diff --git a/crates/next-core/src/base_loader_tree.rs b/crates/next-core/src/base_loader_tree.rs index bbb42f49e661..7d8be54a92e5 100644 --- a/crates/next-core/src/base_loader_tree.rs +++ b/crates/next-core/src/base_loader_tree.rs @@ -15,7 +15,7 @@ use turbopack_ecmascript::{magic_identifier, utils::StringifyJs}; pub struct BaseLoaderTreeBuilder { pub inner_assets: FxIndexMap>>, counter: usize, - pub imports: Vec, + pub imports: Vec<(u32, RcStr)>, pub module_asset_context: ResolvedVc, pub server_component_transition: ResolvedVc>, } @@ -91,12 +91,14 @@ impl BaseLoaderTreeBuilder { &mut self, module_type: AppDirModuleType, path: FileSystemPath, + position: u32, ) -> Result { let name = module_type.name(); let i = self.unique_number(); let identifier = magic_identifier::mangle(&format!("{name} #{i}")); - self.imports.push( + self.imports.push(( + position, formatdoc!( r#" const {} = () => require(/*turbopackChunkingType: shared*/"MODULE_{}"); @@ -105,7 +107,7 @@ impl BaseLoaderTreeBuilder { i ) .into(), - ); + )); let module = self .process_source(Vc::upcast(FileSource::new(path.clone()))) diff --git a/crates/next-napi-bindings/build.rs b/crates/next-napi-bindings/build.rs index fb230f35079a..212c67b304a0 100644 --- a/crates/next-napi-bindings/build.rs +++ b/crates/next-napi-bindings/build.rs @@ -57,9 +57,9 @@ fn main() -> anyhow::Result<()> { ) .build()?; vergen_gitcl::Emitter::default() + .fail_on_error() .add_instructions(&cargo)? .add_instructions(&git)? - .fail_on_error() .emit()?; match Command::new("git").args(["rev-parse", "HEAD"]).output() { @@ -67,7 +67,12 @@ fn main() -> anyhow::Result<()> { "cargo:warning=git HEAD: {}", str::from_utf8(&out.stdout).unwrap() ), - _ => println!("cargo:warning=`git rev-parse HEAD` failed"), + Ok(out) => println!( + "cargo:warning=`git rev-parse HEAD` failed with status {}: {}", + out.status, + str::from_utf8(&out.stderr).unwrap() + ), + Err(e) => println!("cargo:warning=`git rev-parse HEAD` could not be spawned: {e}"), } if !is_macos_target { diff --git a/docs/04-community/01-contribution-guide.mdx b/docs/04-community/01-contribution-guide.mdx index 52c8acfe291a..fc3a4e31992a 100644 --- a/docs/04-community/01-contribution-guide.mdx +++ b/docs/04-community/01-contribution-guide.mdx @@ -243,17 +243,17 @@ Most examples in the docs are written in `tsx` and `jsx`, and a few in `bash`. H When writing JavaScript code blocks, we use the following language and extension combinations. -| | Language | Extension | -| ------------------------------ | -------- | --------- | -| JavaScript files with JSX code | ```jsx | .js | -| JavaScript files without JSX | ```js | .js | -| TypeScript files with JSX | ```tsx | .tsx | -| TypeScript files without JSX | ```ts | .ts | +| | Language | Extension | +| ------------------------------ | ---------- | --------- | +| JavaScript files with JSX code | ` ```jsx ` | .js | +| JavaScript files without JSX | ` ```js ` | .js | +| TypeScript files with JSX | ` ```tsx ` | .tsx | +| TypeScript files without JSX | ` ```ts ` | .ts | > **Good to know**: > > - Make sure to use **`.js`** extension for JavaScript files with **JSX** code. -> - For example, ```jsx filename="app/layout.js" +> - For example, ` ```jsx filename="app/layout.js" ` ### TS and JS Switcher diff --git a/lerna.json b/lerna.json index 5a5b879c6fb9..57fd330c0f3b 100644 --- a/lerna.json +++ b/lerna.json @@ -15,5 +15,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "16.3.0-canary.13" + "version": "16.3.0-canary.14" } \ No newline at end of file diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 4c82b6f617fb..e80964eeb7fc 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 79c4b8a54ab1..5a345b2fd7ac 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "description": "ESLint configuration used by Next.js.", "license": "MIT", "repository": { @@ -12,7 +12,7 @@ "dist" ], "dependencies": { - "@next/eslint-plugin-next": "16.3.0-canary.13", + "@next/eslint-plugin-next": "16.3.0-canary.14", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index dcc2701fe0f1..1bb5097910ee 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -1,7 +1,7 @@ { "name": "@next/eslint-plugin-internal", "private": true, - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "description": "ESLint plugin for working on Next.js.", "exports": { ".": "./src/eslint-plugin-internal.js" diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index 4fe8b37bd2e0..a575959aae04 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": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "description": "ESLint plugin for Next.js.", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/font/package.json b/packages/font/package.json index ebc6de6119e6..e804f2672921 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,7 +1,7 @@ { "name": "@next/font", "private": true, - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "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 f57a46b98257..2cac73f0253a 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 067f4f12a87a..fe5aa6197c95 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index b6635b0ca4a3..2b49ecbf1253 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index fa25a872cbc6..ae15231701da 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-playwright/package.json b/packages/next-playwright/package.json index 5a9646983ea1..7df5603c4fbf 100644 --- a/packages/next-playwright/package.json +++ b/packages/next-playwright/package.json @@ -1,6 +1,6 @@ { "name": "@next/playwright", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "repository": { "url": "vercel/next.js", "directory": "packages/next-playwright" diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index b1e610593d0f..01e47424f4d6 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "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 44d372e60dca..3f64a3d32ed1 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "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 d12a2c39c88c..fea6fe6c9dc3 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-routing/package.json b/packages/next-routing/package.json index 342f33d6a39f..f42ea9c3aa8b 100644 --- a/packages/next-routing/package.json +++ b/packages/next-routing/package.json @@ -1,6 +1,6 @@ { "name": "@next/routing", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "keywords": [ "react", "next", diff --git a/packages/next-rspack/package.json b/packages/next-rspack/package.json index fbf3dfd76999..d8f302b9182a 100644 --- a/packages/next-rspack/package.json +++ b/packages/next-rspack/package.json @@ -1,6 +1,6 @@ { "name": "next-rspack", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "repository": { "url": "vercel/next.js", "directory": "packages/next-rspack" diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index 8b7770d729de..240a72ce406b 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "private": true, "files": [ "native/" diff --git a/packages/next/package.json b/packages/next/package.json index f60b29dcbc19..c799202b8638 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "16.3.0-canary.13", + "version": "16.3.0-canary.14", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -101,7 +101,7 @@ ] }, "dependencies": { - "@next/env": "16.3.0-canary.13", + "@next/env": "16.3.0-canary.14", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -165,11 +165,11 @@ "@modelcontextprotocol/sdk": "1.18.1", "@mswjs/interceptors": "0.23.0", "@napi-rs/triples": "1.2.0", - "@next/font": "16.3.0-canary.13", - "@next/polyfill-module": "16.3.0-canary.13", - "@next/polyfill-nomodule": "16.3.0-canary.13", - "@next/react-refresh-utils": "16.3.0-canary.13", - "@next/swc": "16.3.0-canary.13", + "@next/font": "16.3.0-canary.14", + "@next/polyfill-module": "16.3.0-canary.14", + "@next/polyfill-nomodule": "16.3.0-canary.14", + "@next/react-refresh-utils": "16.3.0-canary.14", + "@next/swc": "16.3.0-canary.14", "@opentelemetry/api": "1.6.0", "@playwright/test": "1.58.2", "@rspack/core": "1.6.7", diff --git a/packages/next/src/server/dev/middleware-turbopack.ts b/packages/next/src/server/dev/middleware-turbopack.ts index 5ad2701b1ef6..a3a9247167bd 100644 --- a/packages/next/src/server/dev/middleware-turbopack.ts +++ b/packages/next/src/server/dev/middleware-turbopack.ts @@ -283,10 +283,22 @@ async function nativeTraceSource( return undefined } +/** + * Code frame rendering options. The defaults match terminal consumers; only + * the overlay HTTP path opts in to always-on colors and the wide max width. + */ +type CodeFrameOptions = { + /** Defaults to `process.stdout.isTTY`. */ + colors?: boolean + /** Defaults to the dev server's terminal width. */ + maxWidth?: number +} + async function createOriginalStackFrame( project: Project, projectPath: string, - frame: TurbopackStackFrame + frame: TurbopackStackFrame, + codeFrameOptions?: CodeFrameOptions ): Promise { const traced = (await nativeTraceSource(frame)) ?? @@ -324,9 +336,8 @@ async function createOriginalStackFrame( get originalCodeFrame() { if (originalCodeFrame === undefined) { originalCodeFrame = getOriginalCodeFrame(tracedFrame, traced.source, { - // The overlay renders in a browser with horizontal scrolling, - // so don't truncate lines to the server's terminal width. - maxWidth: DEVTOOLS_CODE_FRAME_MAX_WIDTH, + colors: codeFrameOptions?.colors, + maxWidth: codeFrameOptions?.maxWidth, }) } return originalCodeFrame @@ -372,6 +383,13 @@ export function getOverlayMiddleware({ isServer: request.isServer, isEdgeServer: request.isEdgeServer, isAppDirectory: request.isAppDirectory, + codeFrameOptions: { + // Overlay parses ANSI in JS and renders in a scrollable + // `
`, so colors are always wanted and terminal width is
+          // irrelevant.
+          colors: true,
+          maxWidth: DEVTOOLS_CODE_FRAME_MAX_WIDTH,
+        },
       })
 
       ignoreListAnonymousStackFramesIfSandwiched(result)
@@ -492,6 +510,7 @@ export async function getOriginalStackFrames({
   isServer,
   isEdgeServer,
   isAppDirectory,
+  codeFrameOptions,
 }: {
   project: Project
   projectPath: string
@@ -499,6 +518,7 @@ export async function getOriginalStackFrames({
   isServer: boolean
   isEdgeServer: boolean
   isAppDirectory: boolean
+  codeFrameOptions?: CodeFrameOptions
 }): Promise {
   const stackFrames = createStackFrames({
     frames,
@@ -513,7 +533,8 @@ export async function getOriginalStackFrames({
         const stackFrame = await createOriginalStackFrame(
           project,
           projectPath,
-          frame
+          frame,
+          codeFrameOptions
         )
         if (stackFrame === null) {
           return {
diff --git a/packages/next/src/server/dev/middleware-webpack.ts b/packages/next/src/server/dev/middleware-webpack.ts
index a2e9fad69aec..556cb652497d 100644
--- a/packages/next/src/server/dev/middleware-webpack.ts
+++ b/packages/next/src/server/dev/middleware-webpack.ts
@@ -182,12 +182,24 @@ function findOriginalSourcePositionAndContentFromCompilation(
   return module?.buildInfo?.importLocByPath?.get(importedModule) ?? null
 }
 
+/**
+ * Code frame rendering options. The defaults match terminal consumers; only
+ * the overlay HTTP path opts in to always-on colors and the wide max width.
+ */
+type CodeFrameOptions = {
+  /** Defaults to `process.stdout.isTTY`. */
+  colors?: boolean
+  /** Defaults to the dev server's terminal width. */
+  maxWidth?: number
+}
+
 export async function createOriginalStackFrame({
   ignoredByDefault,
   source,
   rootDirectory,
   frame,
   errorMessage,
+  codeFrameOptions,
 }: {
   /** setting this to true will not consult ignoreList */
   ignoredByDefault: boolean
@@ -195,6 +207,7 @@ export async function createOriginalStackFrame({
   rootDirectory: string
   frame: StackFrame
   errorMessage?: string
+  codeFrameOptions?: CodeFrameOptions
 }): Promise {
   const moduleNotFound = findModuleNotFoundFromError(errorMessage)
   const result = await (() => {
@@ -263,9 +276,8 @@ export async function createOriginalStackFrame({
     get originalCodeFrame() {
       if (originalCodeFrame === undefined) {
         originalCodeFrame = getOriginalCodeFrame(traced, sourceContent, {
-          // The overlay renders in a browser with horizontal scrolling,
-          // so don't truncate lines to the server's terminal width.
-          maxWidth: DEVTOOLS_CODE_FRAME_MAX_WIDTH,
+          colors: codeFrameOptions?.colors,
+          maxWidth: codeFrameOptions?.maxWidth,
         })
       }
       return originalCodeFrame
@@ -393,6 +405,7 @@ export async function getOriginalStackFrames({
   serverStats,
   edgeServerStats,
   rootDirectory,
+  codeFrameOptions,
 }: {
   isServer: boolean
   isEdgeServer: boolean
@@ -402,6 +415,7 @@ export async function getOriginalStackFrames({
   serverStats: () => webpack.Stats | null
   edgeServerStats: () => webpack.Stats | null
   rootDirectory: string
+  codeFrameOptions?: CodeFrameOptions
 }): Promise {
   const frameResponses = await Promise.all(
     frames.map(
@@ -415,6 +429,7 @@ export async function getOriginalStackFrames({
           serverStats,
           edgeServerStats,
           rootDirectory,
+          codeFrameOptions,
         }).then(
           (value) => {
             return {
@@ -446,6 +461,7 @@ async function getOriginalStackFrame({
   serverStats,
   edgeServerStats,
   rootDirectory,
+  codeFrameOptions,
 }: {
   isServer: boolean
   isEdgeServer: boolean
@@ -455,6 +471,7 @@ async function getOriginalStackFrame({
   serverStats: () => webpack.Stats | null
   edgeServerStats: () => webpack.Stats | null
   rootDirectory: string
+  codeFrameOptions?: CodeFrameOptions
 }): Promise {
   const filename = frame.file ?? ''
   const source = await getSource(frame, {
@@ -534,6 +551,7 @@ async function getOriginalStackFrame({
     frame,
     source,
     rootDirectory,
+    codeFrameOptions,
   })
 
   if (!originalStackFrameResponse) {
@@ -601,6 +619,13 @@ export function getOverlayMiddleware(options: {
             serverStats,
             edgeServerStats,
             rootDirectory,
+            codeFrameOptions: {
+              // Overlay parses ANSI in JS and renders in a scrollable
+              // `
`, so colors are always wanted and terminal width
+              // is irrelevant.
+              colors: true,
+              maxWidth: DEVTOOLS_CODE_FRAME_MAX_WIDTH,
+            },
           })
         )
       } catch (err) {
diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json
index 24a5aff3a985..5e43c526ac09 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": "16.3.0-canary.13",
+  "version": "16.3.0-canary.14",
   "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 b6d745f1b8a2..64458dcd868d 100644
--- a/packages/third-parties/package.json
+++ b/packages/third-parties/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@next/third-parties",
-  "version": "16.3.0-canary.13",
+  "version": "16.3.0-canary.14",
   "repository": {
     "url": "vercel/next.js",
     "directory": "packages/third-parties"
@@ -27,7 +27,7 @@
     "third-party-capital": "1.0.20"
   },
   "devDependencies": {
-    "next": "16.3.0-canary.13",
+    "next": "16.3.0-canary.14",
     "outdent": "0.8.0",
     "prettier": "2.5.1",
     "typescript": "6.0.2"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ae23c6049b83..75ca9e7ab5c6 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -983,7 +983,7 @@ importers:
   packages/eslint-config-next:
     dependencies:
       '@next/eslint-plugin-next':
-        specifier: 16.3.0-canary.13
+        specifier: 16.3.0-canary.14
         version: link:../eslint-plugin-next
       eslint:
         specifier: '>=9.0.0'
@@ -1060,7 +1060,7 @@ importers:
   packages/next:
     dependencies:
       '@next/env':
-        specifier: 16.3.0-canary.13
+        specifier: 16.3.0-canary.14
         version: link:../next-env
       '@swc/helpers':
         specifier: 0.5.15
@@ -1181,19 +1181,19 @@ importers:
         specifier: 1.2.0
         version: 1.2.0
       '@next/font':
-        specifier: 16.3.0-canary.13
+        specifier: 16.3.0-canary.14
         version: link:../font
       '@next/polyfill-module':
-        specifier: 16.3.0-canary.13
+        specifier: 16.3.0-canary.14
         version: link:../next-polyfill-module
       '@next/polyfill-nomodule':
-        specifier: 16.3.0-canary.13
+        specifier: 16.3.0-canary.14
         version: link:../next-polyfill-nomodule
       '@next/react-refresh-utils':
-        specifier: 16.3.0-canary.13
+        specifier: 16.3.0-canary.14
         version: link:../react-refresh-utils
       '@next/swc':
-        specifier: 16.3.0-canary.13
+        specifier: 16.3.0-canary.14
         version: link:../next-swc
       '@opentelemetry/api':
         specifier: 1.6.0
@@ -1927,7 +1927,7 @@ importers:
         version: 1.0.20
     devDependencies:
       next:
-        specifier: 16.3.0-canary.13
+        specifier: 16.3.0-canary.14
         version: link:../next
       outdent:
         specifier: 0.8.0
diff --git a/scripts/docker-native-build.sh b/scripts/docker-native-build.sh
index 7f9619fd5045..c77181219592 100755
--- a/scripts/docker-native-build.sh
+++ b/scripts/docker-native-build.sh
@@ -16,6 +16,10 @@
 
 set -xeo pipefail
 
+# /build is bind-mounted from the host with a uid that differs from the
+# container's root user; mark it safe so vergen's `git rev-parse` works.
+git config --global --add safe.directory /build
+
 BUILD_TASK="${BUILD_TASK:-build-native-release}"
 
 # Node.js (installed via nodesource) is used only as a build tool (runs
diff --git a/test/e2e/app-dir/nx-handling/nx-handling.test.ts b/test/e2e/app-dir/nx-handling/nx-handling.test.ts
index 9e373efa8cdb..548144c0abd6 100644
--- a/test/e2e/app-dir/nx-handling/nx-handling.test.ts
+++ b/test/e2e/app-dir/nx-handling/nx-handling.test.ts
@@ -11,6 +11,7 @@ describe('nx-handling', () => {
       name: '@nx-next/source',
       version: '0.0.0',
       private: true,
+      packageManager: 'npm@10.9.2',
       scripts: {
         build: 'rm -rf dist; nx run next-nx-test:build',
         dev: 'nx run next-nx-test:dev',
diff --git a/test/e2e/filesystem-cache/filesystem-cache.test.ts b/test/e2e/filesystem-cache/filesystem-cache.test.ts
index dece69e81029..283056869860 100644
--- a/test/e2e/filesystem-cache/filesystem-cache.test.ts
+++ b/test/e2e/filesystem-cache/filesystem-cache.test.ts
@@ -48,6 +48,7 @@ for (const cacheEnabled of [false, true]) {
       files: __dirname,
       skipDeployment: true,
       packageJson: {
+        packageManager: 'npm@10.9.2',
         scripts: {
           build: `${envVars} next build`,
           dev: `${envVars} next dev`,
diff --git a/test/e2e/handle-non-hoisted-swc-helpers/index.test.ts b/test/e2e/handle-non-hoisted-swc-helpers/index.test.ts
index dc9927231afe..89059f33b388 100644
--- a/test/e2e/handle-non-hoisted-swc-helpers/index.test.ts
+++ b/test/e2e/handle-non-hoisted-swc-helpers/index.test.ts
@@ -1,4 +1,4 @@
-import { createNext } from 'e2e-utils'
+import { createNext, isNextDev } from 'e2e-utils'
 import { NextInstance } from 'e2e-utils'
 import { renderViaHTTP } from 'next-test-utils'
 
@@ -24,8 +24,18 @@ describe('handle-non-hoisted-swc-helpers', () => {
           }
         `,
       },
+      packageJson: {
+        packageManager: 'npm@10.9.2',
+        scripts: {
+          build: 'next build',
+          dev: 'next dev',
+          start: 'next start',
+        },
+      },
       installCommand:
         'npm install; mkdir -p node_modules/next/node_modules/@swc; mv node_modules/@swc/helpers node_modules/next/node_modules/@swc/',
+      buildCommand: 'npm run build',
+      startCommand: isNextDev ? 'npm run dev' : 'npm run start',
       dependencies: {},
     })
   })
diff --git a/test/e2e/yarn-pnp/test/utils.ts b/test/e2e/yarn-pnp/test/utils.ts
index 122a07ccf3ce..5cda96939550 100644
--- a/test/e2e/yarn-pnp/test/utils.ts
+++ b/test/e2e/yarn-pnp/test/utils.ts
@@ -45,6 +45,11 @@ export function runTests(
           },
           {} as { [key: string]: FileRef }
         ),
+        packageJson: {
+          // Bootstrap with classic yarn; the install command below runs
+          // `yarn set version berry` which rewrites this to the berry version.
+          packageManager: 'yarn@1.22.22',
+        },
         dependencies: {
           ...packageJson.dependencies,
           ...packageJson.devDependencies,
diff --git a/test/integration/create-next-app/package-manager/pnpm.test.ts b/test/integration/create-next-app/package-manager/pnpm.test.ts
index 726553762039..e9f074d944bf 100644
--- a/test/integration/create-next-app/package-manager/pnpm.test.ts
+++ b/test/integration/create-next-app/package-manager/pnpm.test.ts
@@ -1,4 +1,5 @@
 import {
+  command,
   DEFAULT_FILES,
   FULL_EXAMPLE_PATH,
   projectFilesShouldExist,
@@ -10,6 +11,11 @@ import {
 const lockFile = 'pnpm-lock.yaml'
 const files = [...DEFAULT_FILES, lockFile]
 
+// Match the monorepo's pinned pnpm so CNA's `pnpm install` doesn't drift to
+// whichever version corepack happens to fetch as "latest" at test time.
+const rootPackageManager: string =
+  require('../../../../package.json').packageManager
+
 describe('create-next-app with package manager pnpm', () => {
   let nextTgzFilename: string
 
@@ -18,6 +24,8 @@ describe('create-next-app with package manager pnpm', () => {
       throw new Error('This test needs to be run with `node run-tests.js`.')
     }
 
+    await command('corepack', ['prepare', '--activate', rootPackageManager])
+
     const pkgPaths = new Map(
       JSON.parse(process.env.NEXT_TEST_PKG_PATHS)
     )
diff --git a/test/lib/create-next-install.js b/test/lib/create-next-install.js
index e55eb0896b9b..27c0aabee13f 100644
--- a/test/lib/create-next-install.js
+++ b/test/lib/create-next-install.js
@@ -9,6 +9,7 @@ const { linkPackages } =
 
 const PREFER_OFFLINE = process.env.NEXT_TEST_PREFER_OFFLINE === '1'
 const useRspack = process.env.NEXT_TEST_USE_RSPACK === '1'
+const ROOT_PACKAGE_MANAGER = require('../../package.json').packageManager
 
 async function installDependencies(cwd, tmpDir) {
   const args = [
@@ -175,6 +176,10 @@ async function createNextInstall({
         path.join(installDir, 'package.json'),
         JSON.stringify(
           {
+            // Pin packageManager so corepack doesn't auto-inject a reference
+            // to the latest version (and rewrite this file mid-test).
+            // Callers can override via packageJson.packageManager.
+            packageManager: ROOT_PACKAGE_MANAGER,
             ...packageJson,
             scripts,
             dependencies: combinedDependencies,
diff --git a/test/lib/next-modes/base.ts b/test/lib/next-modes/base.ts
index 7851d194d78c..a4b51a836a9e 100644
--- a/test/lib/next-modes/base.ts
+++ b/test/lib/next-modes/base.ts
@@ -71,6 +71,9 @@ type OmitFirstArgument = F extends (
 // prettier-ignore
 const nextjsReactPeerVersion = "19.2.6";
 
+const ROOT_PACKAGE_MANAGER: string =
+  require('../../../package.json').packageManager
+
 export class NextInstance {
   protected files: ResolvedFileConfig
   protected overrideFiles: ResolvedFileConfig
@@ -275,6 +278,10 @@ export class NextInstance {
             path.join(this.testDir, 'package.json'),
             JSON.stringify(
               {
+                // Pin packageManager so corepack doesn't auto-inject a reference
+                // to the latest version (and rewrite this file mid-test).
+                // Callers can override via packageJson.packageManager.
+                packageManager: ROOT_PACKAGE_MANAGER,
                 ...this.packageJson,
                 dependencies: {
                   ...finalDependencies,
diff --git a/turbopack/crates/turbopack-cli/build.rs b/turbopack/crates/turbopack-cli/build.rs
index 3840ad52c040..cfc3e3466db0 100644
--- a/turbopack/crates/turbopack-cli/build.rs
+++ b/turbopack/crates/turbopack-cli/build.rs
@@ -28,8 +28,8 @@ fn main() -> anyhow::Result<()> {
         )
         .build()?;
     vergen_gitcl::Emitter::default()
-        .add_instructions(&git)?
         .fail_on_error()
+        .add_instructions(&git)?
         .emit()?;
 
     Ok(())