diff --git a/.eslintrc.json b/.eslintrc.json index 8c46d8c02736f..feb49b2ecbbff 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -162,7 +162,10 @@ }, { "files": ["packages/**"], - "excludedFiles": ["packages/next/taskfile.js"], + "excludedFiles": [ + "packages/next/taskfile*.js", + "packages/next/webpack.config.js" + ], "rules": { "no-shadow": ["warn", { "builtinGlobals": false }], "import/no-extraneous-dependencies": [ diff --git a/bench/basic-app/app/api/app/route.js b/bench/basic-app/app/api/app/route.js new file mode 100644 index 0000000000000..944ba5a8e827f --- /dev/null +++ b/bench/basic-app/app/api/app/route.js @@ -0,0 +1,5 @@ +export function GET() { + return { name: 'John Doe' } +} + +export const dynamic = 'force-dynamic' diff --git a/bench/basic-app/app/layout.js b/bench/basic-app/app/layout.js new file mode 100644 index 0000000000000..8ebf54889577d --- /dev/null +++ b/bench/basic-app/app/layout.js @@ -0,0 +1,12 @@ +import React from 'react' + +export default function Layout({ children }) { + return ( + + + My App + + {children} + + ) +} diff --git a/bench/basic-app/app/page.js b/bench/basic-app/app/page.js new file mode 100644 index 0000000000000..83dc3aa56c9a0 --- /dev/null +++ b/bench/basic-app/app/page.js @@ -0,0 +1,7 @@ +import React from 'react' + +export default function Page() { + return

My Page

+} + +export const dynamic = 'force-dynamic' diff --git a/bench/basic-app/next.config.js b/bench/basic-app/next.config.js new file mode 100644 index 0000000000000..0957c472383fa --- /dev/null +++ b/bench/basic-app/next.config.js @@ -0,0 +1,5 @@ +module.exports = { + experimental: { + serverMinification: true, + }, +} diff --git a/bench/basic-app/pages/api/index.js b/bench/basic-app/pages/api/index.js new file mode 100644 index 0000000000000..8f603094bd288 --- /dev/null +++ b/bench/basic-app/pages/api/index.js @@ -0,0 +1,3 @@ +export default function handler(req, res) { + res.status(200).json({ name: 'John Doe' }) +} diff --git a/bench/basic-app/pages/pages/index.js b/bench/basic-app/pages/pages/index.js new file mode 100644 index 0000000000000..e06229eee0637 --- /dev/null +++ b/bench/basic-app/pages/pages/index.js @@ -0,0 +1,7 @@ +export default () => 'Hello World' + +export function getServerSideProps() { + return { + props: {}, + } +} diff --git a/packages/next-swc/crates/next-core/js/src/entry/app-edge-renderer.tsx b/packages/next-swc/crates/next-core/js/src/entry/app-edge-renderer.tsx index fdec9ffc360fe..38f27b3c1c7a3 100644 --- a/packages/next-swc/crates/next-core/js/src/entry/app-edge-renderer.tsx +++ b/packages/next-swc/crates/next-core/js/src/entry/app-edge-renderer.tsx @@ -2,6 +2,8 @@ // the other imports import startOperationStreamHandler from '../internal/operation-stream' +import 'next/dist/server/node-polyfill-fetch' + import { join } from 'path' import { parse as parseUrl } from 'node:url' diff --git a/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx b/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx index 951f91570d92d..25437fec08ef4 100644 --- a/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx +++ b/packages/next-swc/crates/next-core/js/src/entry/app-renderer.tsx @@ -3,13 +3,15 @@ import startOperationStreamHandler from '../internal/operation-stream' import '../polyfill/app-polyfills.ts' +// TODO: when actions are supported, this should be removed/changed +process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = 'next' +import 'next/dist/server/require-hook' import type { IncomingMessage } from 'node:http' import type { RenderData } from 'types/turbopack' import type { RenderOpts } from 'next/dist/server/app-render/types' -import { renderToHTMLOrFlight } from 'next/dist/server/app-render/app-render' import { RSC_VARY_HEADER } from 'next/dist/client/components/app-router-headers' import { headersFromEntries, initProxiedHeaders } from '../internal/headers' import { parse, ParsedUrlQuery } from 'node:querystring' @@ -23,6 +25,10 @@ import { join } from 'node:path' import { nodeFs } from 'next/dist/server/lib/node-fs-methods' import { IncrementalCache } from 'next/dist/server/lib/incremental-cache' +const { + renderToHTMLOrFlight, +} = require('next/dist/compiled/next-server/app-page.runtime.dev') + installRequireAndChunkLoad() const MIME_TEXT_HTML_UTF8 = 'text/html; charset=utf-8' diff --git a/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx b/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx index abdf23d0fd7d3..c4e1bce96aa5e 100644 --- a/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx +++ b/packages/next-swc/crates/next-core/js/src/entry/app/hydrate.tsx @@ -6,7 +6,7 @@ import { createFromReadableStream } from 'next/dist/compiled/react-server-dom-we import { callServer } from 'next/dist/client/app-call-server' import { linkGc } from 'next/dist/client/app-link-gc' -import { HeadManagerContext } from 'next/dist/shared/lib/head-manager-context' +import { HeadManagerContext } from 'next/dist/shared/lib/head-manager-context.shared-runtime' import { initializeHMR } from '@vercel/turbopack-next/dev/client' diff --git a/packages/next-swc/crates/next-core/js/src/internal/page-server-handler.tsx b/packages/next-swc/crates/next-core/js/src/internal/page-server-handler.tsx index 6182b2d7d4539..cecafb35f77a7 100644 --- a/packages/next-swc/crates/next-core/js/src/internal/page-server-handler.tsx +++ b/packages/next-swc/crates/next-core/js/src/internal/page-server-handler.tsx @@ -3,11 +3,12 @@ import { IPC } from '@vercel/turbopack-node/ipc/index' import 'next/dist/server/node-polyfill-fetch.js' +import 'next/dist/server/require-hook' import './shims' import type { IncomingMessage, ServerResponse } from 'node:http' -import { renderToHTML, RenderOpts } from 'next/dist/server/render' +import type { RenderOpts } from 'next/dist/server/render' import { getRedirectStatus } from 'next/dist/lib/redirect-status' import { PERMANENT_REDIRECT_STATUS } from 'next/dist/shared/lib/constants' import { buildStaticPaths } from 'next/dist/build/utils' @@ -21,6 +22,9 @@ import type { RenderData } from 'types/turbopack' import type { ChunkGroup } from 'types/next' import type { NextComponentType } from 'next/types' import { parse } from 'node:querystring' +const { + renderToHTML, +} = require('next/dist/compiled/next-server/pages.runtime.dev') const ipc = IPC as Ipc diff --git a/packages/next-swc/crates/next-core/src/app_source.rs b/packages/next-swc/crates/next-core/src/app_source.rs index e5af263c3d6bd..738f5a4bf938f 100644 --- a/packages/next-swc/crates/next-core/src/app_source.rs +++ b/packages/next-swc/crates/next-core/src/app_source.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, io::Write as _, iter::once}; use anyhow::{bail, Result}; use indexmap::indexmap; -use indoc::indoc; +use indoc::formatdoc; use serde_json::Value as JsonValue; use turbo_tasks::Vc; use turbopack_binding::{ @@ -968,13 +968,18 @@ impl AppRenderer { .emit(); } - let mut result = RopeBuilder::from(indoc! {" - \"TURBOPACK { chunking-type: isolatedParallel; transition: next-edge-server-component }\"; + let mut result = RopeBuilder::from( + formatdoc!( + " + \"TURBOPACK {{ chunking-type: isolatedParallel; transition: {rsc_transition} }}\"; import GlobalErrorMod from \"next/dist/client/components/error-boundary\" - const { GlobalError } = GlobalErrorMod; - \"TURBOPACK { chunking-type: isolatedParallel; transition: next-edge-server-component }\"; + const {{ GlobalError }} = GlobalErrorMod; + \"TURBOPACK {{ chunking-type: isolatedParallel; transition: {rsc_transition} }}\"; import base from \"next/dist/server/app-render/entry-base\"\n - "}); + " + ) + .into_bytes(), + ); for import in loader_tree_module.imports { writeln!(result, "{import}")?; diff --git a/packages/next-swc/crates/next-core/src/next_edge/context.rs b/packages/next-swc/crates/next-core/src/next_edge/context.rs index 54c7dd3331a63..cfe9eebf19f94 100644 --- a/packages/next-swc/crates/next-core/src/next_edge/context.rs +++ b/packages/next-swc/crates/next-core/src/next_edge/context.rs @@ -96,10 +96,9 @@ pub async fn get_edge_resolve_options_context( ]; match ty { - ServerContextType::AppRSC { .. } | ServerContextType::AppRoute { .. } => { - custom_conditions.push("react-server".to_string()) - } - ServerContextType::Pages { .. } + ServerContextType::AppRSC { .. } => custom_conditions.push("react-server".to_string()), + ServerContextType::AppRoute { .. } + | ServerContextType::Pages { .. } | ServerContextType::PagesData { .. } | ServerContextType::AppSSR { .. } | ServerContextType::Middleware { .. } => {} diff --git a/packages/next-swc/crates/next-core/src/next_edge/route_transition.rs b/packages/next-swc/crates/next-core/src/next_edge/route_transition.rs index de0bd2f2e7bf7..05326de255203 100644 --- a/packages/next-swc/crates/next-core/src/next_edge/route_transition.rs +++ b/packages/next-swc/crates/next-core/src/next_edge/route_transition.rs @@ -58,24 +58,26 @@ impl Transition for NextEdgeRouteTransition { #[turbo_tasks::function] async fn process_module( - &self, + self: Vc, asset: Vc>, context: Vc, ) -> Result>> { + let new_context = self.process_context(context); + let this = self.await?; let new_asset = route_bootstrap( asset, - Vc::upcast(context), - self.base_path, - self.bootstrap_asset, + Vc::upcast(new_context), + this.base_path, + this.bootstrap_asset, Vc::cell(indexmap! { - "NAME".to_string() => self.entry_name.clone(), + "NAME".to_string() => this.entry_name.clone(), }), ); let asset = ChunkGroupFilesAsset { module: Vc::upcast(new_asset), - client_root: self.output_path, - chunking_context: self.edge_chunking_context, + client_root: this.output_path, + chunking_context: this.edge_chunking_context, runtime_entries: None, }; diff --git a/packages/next-swc/crates/next-core/src/next_import_map.rs b/packages/next-swc/crates/next-core/src/next_import_map.rs index e05ac926c139f..c530f3bcb9290 100644 --- a/packages/next-swc/crates/next-core/src/next_import_map.rs +++ b/packages/next-swc/crates/next-core/src/next_import_map.rs @@ -216,25 +216,26 @@ pub async fn get_next_server_import_map( let ty = ty.into_value(); insert_next_server_special_aliases(&mut import_map, ty, mode, NextRuntime::NodeJs).await?; - let external = ImportMapping::External(None).cell(); + let external: Vc = ImportMapping::External(None).cell(); + import_map.insert_exact_alias("next/dist/server/require-hook", external); match ty { ServerContextType::Pages { .. } | ServerContextType::PagesData { .. } => { - import_map.insert_exact_alias("next", external); - import_map.insert_wildcard_alias("next/", external); import_map.insert_exact_alias("react", external); import_map.insert_wildcard_alias("react/", external); import_map.insert_exact_alias("react-dom", external); import_map.insert_wildcard_alias("react-dom/", external); import_map.insert_exact_alias("styled-jsx", external); import_map.insert_wildcard_alias("styled-jsx/", external); - import_map.insert_exact_alias("react-server-dom-webpack/", external); + import_map.insert_wildcard_alias("react-server-dom-webpack/", external); + // TODO: we should not bundle next/dist/build/utils in the pages renderer at all + import_map.insert_wildcard_alias("next/dist/build/utils", external); } ServerContextType::AppSSR { .. } | ServerContextType::AppRSC { .. } | ServerContextType::AppRoute { .. } => { match mode { - NextMode::Development | NextMode::Build => { + NextMode::Build => { import_map.insert_wildcard_alias("next/dist/server/", external); import_map.insert_wildcard_alias("next/dist/shared/", external); } @@ -242,6 +243,7 @@ pub async fn get_next_server_import_map( // The sandbox can't be bundled and needs to be external import_map.insert_exact_alias("next/dist/server/web/sandbox", external); } + NextMode::Development => {} } import_map.insert_exact_alias( "next/head", @@ -377,6 +379,11 @@ async fn insert_next_server_special_aliases( NextRuntime::Edge => request_to_import_mapping(context_dir, request), NextRuntime::NodeJs => external_request_to_import_mapping(request), }; + let passthrough_external_if_node = + move |context_dir: Vc, request: &str| match runtime { + NextRuntime::Edge => request_to_import_mapping(context_dir, request), + NextRuntime::NodeJs => ImportMapping::External(None).cell(), + }; match (mode, ty) { (_, ServerContextType::Pages { pages_dir }) => { import_map.insert_exact_alias( @@ -413,12 +420,7 @@ async fn insert_next_server_special_aliases( (_, ServerContextType::PagesData { .. }) => {} // In development, we *always* use the bundled version of React, even in // SSR, since we're bundling Next.js alongside it. - ( - NextMode::DevServer, - ServerContextType::AppSSR { app_dir } - | ServerContextType::AppRSC { app_dir, .. } - | ServerContextType::AppRoute { app_dir }, - ) => { + (NextMode::DevServer, ServerContextType::AppSSR { app_dir }) => { import_map.insert_exact_alias( "@opentelemetry/api", // TODO(WEB-625) this actually need to prefer the local version of @@ -427,28 +429,40 @@ async fn insert_next_server_special_aliases( ); import_map.insert_exact_alias( "react", - request_to_import_mapping(app_dir, "next/dist/compiled/react"), + passthrough_external_if_node(app_dir, "next/dist/compiled/react"), ); import_map.insert_wildcard_alias( "react/", - request_to_import_mapping(app_dir, "next/dist/compiled/react/*"), + passthrough_external_if_node(app_dir, "next/dist/compiled/react/*"), ); import_map.insert_exact_alias( "react-dom", - request_to_import_mapping( + passthrough_external_if_node( app_dir, "next/dist/compiled/react-dom/server-rendering-stub.js", ), ); import_map.insert_wildcard_alias( "react-dom/", - request_to_import_mapping(app_dir, "next/dist/compiled/react-dom/*"), + passthrough_external_if_node(app_dir, "next/dist/compiled/react-dom/*"), + ); + import_map.insert_exact_alias( + "styled-jsx", + passthrough_external_if_node(app_dir, "next/dist/compiled/styled-jsx"), + ); + import_map.insert_wildcard_alias( + "styled-jsx/", + passthrough_external_if_node(app_dir, "next/dist/compiled/styled-jsx/*"), ); import_map.insert_wildcard_alias( "react-server-dom-webpack/", - request_to_import_mapping(app_dir, "next/dist/compiled/react-server-dom-webpack/*"), + passthrough_external_if_node( + app_dir, + "next/dist/compiled/react-server-dom-webpack/*", + ), ); } + // NOTE(alexkirsz) This logic maps loosely to // `next.js/packages/next/src/build/webpack-config.ts`, where: // @@ -460,7 +474,7 @@ async fn insert_next_server_special_aliases( // * passes through (react|react-dom|react-server-dom-webpack)/(.*) to // next/dist/compiled/$1/$2 ( - NextMode::Build | NextMode::Development, + NextMode::Build | NextMode::Development | NextMode::DevServer, ServerContextType::AppRSC { app_dir, .. } | ServerContextType::AppRoute { app_dir }, ) => { import_map.insert_exact_alias( @@ -469,10 +483,20 @@ async fn insert_next_server_special_aliases( // @opentelemetry/api request_to_import_mapping(app_dir, "next/dist/compiled/@opentelemetry/api"), ); - import_map.insert_exact_alias( - "react", - request_to_import_mapping(app_dir, "next/dist/compiled/react/react.shared-subset"), - ); + if matches!(ty, ServerContextType::AppRSC { .. }) { + import_map.insert_exact_alias( + "react", + request_to_import_mapping( + app_dir, + "next/dist/compiled/react/react.shared-subset", + ), + ); + } else { + import_map.insert_exact_alias( + "react", + request_to_import_mapping(app_dir, "next/dist/compiled/react"), + ); + } import_map.insert_exact_alias( "react-dom", request_to_import_mapping( diff --git a/packages/next-swc/crates/next-core/src/next_server/context.rs b/packages/next-swc/crates/next-core/src/next_server/context.rs index e7485ff100ade..1d242498b36df 100644 --- a/packages/next-swc/crates/next-core/src/next_server/context.rs +++ b/packages/next-swc/crates/next-core/src/next_server/context.rs @@ -46,7 +46,10 @@ use crate::{ next_import_map::{get_next_server_import_map, mdx_import_source_file}, next_server::resolve::ExternalPredicate, next_shared::{ - resolve::{ModuleFeatureReportResolvePlugin, UnsupportedModulesResolvePlugin}, + resolve::{ + ModuleFeatureReportResolvePlugin, NextExternalResolvePlugin, + UnsupportedModulesResolvePlugin, + }, transforms::{ emotion::get_emotion_transform_plugin, get_relay_transform_plugin, styled_components::get_styled_components_transform_plugin, @@ -108,10 +111,9 @@ pub async fn get_server_resolve_options_context( let mut custom_conditions = vec![mode.node_env().to_string(), "node".to_string()]; match ty { - ServerContextType::AppRSC { .. } | ServerContextType::AppRoute { .. } => { - custom_conditions.push("react-server".to_string()) - } - ServerContextType::Pages { .. } + ServerContextType::AppRSC { .. } => custom_conditions.push("react-server".to_string()), + ServerContextType::AppRoute { .. } + | ServerContextType::Pages { .. } | ServerContextType::PagesData { .. } | ServerContextType::AppSSR { .. } | ServerContextType::Middleware { .. } => {} @@ -121,12 +123,15 @@ pub async fn get_server_resolve_options_context( ExternalPredicate::AllExcept(next_config.transpile_packages()).cell(), ); + let next_external_plugin = NextExternalResolvePlugin::new(project_path); + let plugins = match ty { ServerContextType::Pages { .. } | ServerContextType::PagesData { .. } => { vec![ Vc::upcast(module_feature_report_resolve_plugin), Vc::upcast(external_cjs_modules_plugin), Vc::upcast(unsupported_modules_resolve_plugin), + Vc::upcast(next_external_plugin), ] } ServerContextType::AppSSR { .. } @@ -137,6 +142,7 @@ pub async fn get_server_resolve_options_context( Vc::upcast(module_feature_report_resolve_plugin), Vc::upcast(server_component_externals_plugin), Vc::upcast(unsupported_modules_resolve_plugin), + Vc::upcast(next_external_plugin), ] } }; diff --git a/packages/next-swc/crates/next-core/src/next_shared/resolve.rs b/packages/next-swc/crates/next-core/src/next_shared/resolve.rs index 013f87f9fd5b6..ea7f044c12cc8 100644 --- a/packages/next-swc/crates/next-core/src/next_shared/resolve.rs +++ b/packages/next-swc/crates/next-core/src/next_shared/resolve.rs @@ -13,7 +13,7 @@ use turbopack_binding::{ parse::Request, pattern::Pattern, plugin::{ResolvePlugin, ResolvePluginCondition}, - ResolveResultOption, + ResolveResult, ResolveResultItem, ResolveResultOption, }, }, }; @@ -102,6 +102,55 @@ impl ResolvePlugin for UnsupportedModulesResolvePlugin { } } +#[turbo_tasks::value] +pub(crate) struct NextExternalResolvePlugin { + root: Vc, +} + +#[turbo_tasks::value_impl] +impl NextExternalResolvePlugin { + #[turbo_tasks::function] + pub fn new(root: Vc) -> Vc { + NextExternalResolvePlugin { root }.cell() + } +} + +#[turbo_tasks::value_impl] +impl ResolvePlugin for NextExternalResolvePlugin { + #[turbo_tasks::function] + fn after_resolve_condition(&self) -> Vc { + ResolvePluginCondition::new( + self.root.root(), + Glob::new( + "**/next/dist/**/*.{external,shared-runtime,runtime.dev,runtime.prod}.js" + .to_string(), + ), + ) + } + + #[turbo_tasks::function] + async fn after_resolve( + &self, + fs_path: Vc, + _context: Vc, + _request: Vc, + ) -> Result> { + let raw_fs_path = &*fs_path.await?; + let path = raw_fs_path.path.to_string(); + // Find the starting index of 'next/dist' and slice from that point. It should + // always be found since the glob pattern above is specific enough. + let starting_index = path.find("next/dist").unwrap(); + // Replace '/esm/' with '/' to match the CJS version of the file. + let modified_path = &path[starting_index..].replace("/esm/", "/"); + Ok(Vc::cell(Some( + ResolveResult::primary(ResolveResultItem::OriginalReferenceTypeExternal( + modified_path.to_string(), + )) + .into(), + ))) + } +} + /// A resolver plugin tracks the usage of certain import paths, emit /// telemetry events if there is a match. #[turbo_tasks::value] diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-3e4dd8.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-3e4dd8.txt new file mode 100644 index 0000000000000..a5ad94c85fb0a --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-3e4dd8.txt @@ -0,0 +1,17 @@ +warning - [parse] [project]/packages/next/dist/server/web/sandbox/context.js /packages/next/dist/server/web/sandbox/context.js:64:56 lint TP1004 fs.readFile(???*0*) is very dynamic + 60 | } + 61 | async function loadWasm(wasm) { + 62 | const modules = {}; + 63 | await Promise.all(wasm.map(async (binding)=>{ + + v + 64 + const module1 = await WebAssembly.compile(await _fs.promises.readFile(binding.filePath)); + + ^ + 65 | modules[binding.name] = module1; + 66 | })); + 67 | return modules; + 68 | } + + - *0* ???*1*["filePath"] + ⚠️ unknown object + - *1* binding + ⚠️ pattern without value \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-e11df4.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-e11df4.txt new file mode 100644 index 0000000000000..c80db085946c2 --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/api/basic/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-e11df4.txt @@ -0,0 +1,15 @@ +warning - [parse] [project]/packages/next/dist/server/web/sandbox/context.js /packages/next/dist/server/web/sandbox/context.js:355:28 lint TP1004 fs.readFileSync(???*0*, "utf-8") is very dynamic + 351 | } + 352 | const moduleContext = lazyModuleContext; + 353 | const evaluateInContext = (filepath)=>{ + 354 | if (!moduleContext.paths.has(filepath)) { + + v + 355 + const content = (0, _fs.readFileSync)(filepath, "utf-8"); + + ^ + 356 | try { + 357 | (0, _vm.runInContext)(content, moduleContext.runtime.context, { + 358 | filename: filepath + 359 | }); + + - *0* filepath + ⚠️ pattern without value \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-8ad1c9.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-8ad1c9.txt new file mode 100644 index 0000000000000..39e97b4ccc85e --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-8ad1c9.txt @@ -0,0 +1,47 @@ +error - [rendering] [root of the server]/invalid Error during SSR Rendering + Error: Invalid src prop (https://image-optimization-test.vercel.app/test.webp) on `next/image`, hostname "image-optimization-test.vercel.app" is not configured under images in your `next.config.js` + + Debug info: + - Error: Invalid src prop (https://image-optimization-test.vercel.app/test.webp) on `next/image`, hostname "image-optimization-test.vercel.app" is not configured under images in your `next.config.js` + See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host + at defaultLoader (packages/next/dist/shared/lib/image-loader.js:41:27) + 37 | process.env.NEXT_RUNTIME !== "edge") { + 38 | // We use dynamic require because this should only error in development + 39 | const { hasMatch } = require("./match-remote-pattern"); + 40 | if (!hasMatch(config.domains, config.remotePatterns, parsedSrc)) { + | v + 41 + throw new Error("Invalid src prop (" + src + ') on `next/image`, hostname "' + parsedSrc.hostname + '" i...xt.config.js`\n' + "See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host"); + | ^ + 42 | } + 43 | } + 44 | } + 45 | } + + at (packages/next/dist/shared/lib/get-img-props.js:101:36) + 97 | const { widths , kind } = getWidths(config, width, sizes); + 98 | const last = widths.length - 1; + 99 | return { + 100 | sizes: !sizes && kind === "w" ? "100vw" : sizes, + | v + 101 + srcSet: widths.map((w, i)=>loader({ + | ^ + 102 | config, + 103 | src, + 104 | quality, + 105 | width: w + + at generateImgAttrs (packages/next/dist/shared/lib/get-img-props.js:101:24) + 97 | const { widths , kind } = getWidths(config, width, sizes); + 98 | const last = widths.length - 1; + 99 | return { + 100 | sizes: !sizes && kind === "w" ? "100vw" : sizes, + | v + 101 + srcSet: widths.map((w, i)=>loader({ + | ^ + 102 | config, + 103 | src, + 104 | quality, + 105 | width: w + + at getImgProps (packages/next/dist/shared/lib/get-img-props.js:392:27) + at (packages/next/dist/client/image-component.js:275:82) \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-d9114a.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-d9114a.txt deleted file mode 100644 index ce775cb8df7e8..0000000000000 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/image/remotepattern/issues/Error during SSR Rendering-d9114a.txt +++ /dev/null @@ -1,6 +0,0 @@ -error - [rendering] [root of the server]/invalid Error during SSR Rendering - Error: Invalid src prop (https://image-optimization-test.vercel.app/test.webp) on `next/image`, hostname "image-optimization-test.vercel.app" is not configured under images in your `next.config.js` - - Debug info: - - Error: Invalid src prop (https://image-optimization-test.vercel.app/test.webp) on `next/image`, hostname "image-optimization-test.vercel.app" is not configured under images in your `next.config.js` - See more info: https://nextjs.org/docs/messages/next-image-unconfigured-host \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/input/app/test.js b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/input/app/test.js index e2c2f40a06dd7..108b763da879c 100644 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/input/app/test.js +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/input/app/test.js @@ -120,7 +120,7 @@ function runTests() { expect(json).toMatchObject({ edgeThenNode: 'node', nodeThenEdge: 'node', - reactServer: 'react-server', + reactServer: 'default', }) }) @@ -129,7 +129,7 @@ function runTests() { expect(json).toMatchObject({ edgeThenNode: 'edge', nodeThenEdge: 'edge', - reactServer: 'react-server', + reactServer: 'default', }) }) diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-b2593b.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-b2593b.txt deleted file mode 100644 index 72c048d7b6481..0000000000000 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-b2593b.txt +++ /dev/null @@ -1,13 +0,0 @@ -error - [resolve] [project]/packages/next/dist/compiled/nanoid/index.cjs /packages/next/dist/compiled/nanoid/index.cjs:1:45 Error resolving commonjs request - + v---------------v - 1 + (()=>{var e={113:e=>{"use strict";e.exports=require("crypto")},660:(e,r,t)=>{let l=t(113);let{urlAlphabet:a}=t(591);const n=128;let _,u;let fillPool=e=>{if(!_||...ndefined")__nccwpck_require__.ab=__dirname+"/";var t=__nccwpck_require__(660);module.exports=t})(); - + ^---------------^ - - unable to resolve module "crypto" - - | It was not possible to find the requested file. - | Parsed request as written in source code: module "crypto" - | Path where resolving has started: [project]/packages/next/dist/compiled/nanoid/index.cjs - | Type of request: commonjs request - | Import map: No import map entry - | \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-dd84e7.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-dd84e7.txt deleted file mode 100644 index 72c048d7b6481..0000000000000 --- a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/Error resolving commonjs request-dd84e7.txt +++ /dev/null @@ -1,13 +0,0 @@ -error - [resolve] [project]/packages/next/dist/compiled/nanoid/index.cjs /packages/next/dist/compiled/nanoid/index.cjs:1:45 Error resolving commonjs request - + v---------------v - 1 + (()=>{var e={113:e=>{"use strict";e.exports=require("crypto")},660:(e,r,t)=>{let l=t(113);let{urlAlphabet:a}=t(591);const n=128;let _,u;let fillPool=e=>{if(!_||...ndefined")__nccwpck_require__.ab=__dirname+"/";var t=__nccwpck_require__(660);module.exports=t})(); - + ^---------------^ - - unable to resolve module "crypto" - - | It was not possible to find the requested file. - | Parsed request as written in source code: module "crypto" - | Path where resolving has started: [project]/packages/next/dist/compiled/nanoid/index.cjs - | Type of request: commonjs request - | Import map: No import map entry - | \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-76c34b.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-76c34b.txt new file mode 100644 index 0000000000000..a5ad94c85fb0a --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFile(__q____q____q____star__0__-76c34b.txt @@ -0,0 +1,17 @@ +warning - [parse] [project]/packages/next/dist/server/web/sandbox/context.js /packages/next/dist/server/web/sandbox/context.js:64:56 lint TP1004 fs.readFile(???*0*) is very dynamic + 60 | } + 61 | async function loadWasm(wasm) { + 62 | const modules = {}; + 63 | await Promise.all(wasm.map(async (binding)=>{ + + v + 64 + const module1 = await WebAssembly.compile(await _fs.promises.readFile(binding.filePath)); + + ^ + 65 | modules[binding.name] = module1; + 66 | })); + 67 | return modules; + 68 | } + + - *0* ???*1*["filePath"] + ⚠️ unknown object + - *1* binding + ⚠️ pattern without value \ No newline at end of file diff --git a/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-f7e52c.txt b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-f7e52c.txt new file mode 100644 index 0000000000000..c80db085946c2 --- /dev/null +++ b/packages/next-swc/crates/next-dev-tests/tests/integration/next/import/conditions/issues/lint TP1004 fs.readFileSync(__q____q____q____star_-f7e52c.txt @@ -0,0 +1,15 @@ +warning - [parse] [project]/packages/next/dist/server/web/sandbox/context.js /packages/next/dist/server/web/sandbox/context.js:355:28 lint TP1004 fs.readFileSync(???*0*, "utf-8") is very dynamic + 351 | } + 352 | const moduleContext = lazyModuleContext; + 353 | const evaluateInContext = (filepath)=>{ + 354 | if (!moduleContext.paths.has(filepath)) { + + v + 355 + const content = (0, _fs.readFileSync)(filepath, "utf-8"); + + ^ + 356 | try { + 357 | (0, _vm.runInContext)(content, moduleContext.runtime.context, { + 358 | filename: filepath + 359 | }); + + - *0* filepath + ⚠️ pattern without value \ No newline at end of file diff --git a/packages/next/config.d.ts b/packages/next/config.d.ts index 78fe148a8dc9b..2da1ee3c4029c 100644 --- a/packages/next/config.d.ts +++ b/packages/next/config.d.ts @@ -1,3 +1,3 @@ -import getConfig from './dist/shared/lib/runtime-config' -export * from './dist/shared/lib/runtime-config' +import getConfig from './dist/shared/lib/runtime-config.shared-runtime' +export * from './dist/shared/lib/runtime-config.shared-runtime' export default getConfig diff --git a/packages/next/config.js b/packages/next/config.js index 2da980d8b0065..6510748638097 100644 --- a/packages/next/config.js +++ b/packages/next/config.js @@ -1 +1 @@ -module.exports = require('./dist/shared/lib/runtime-config') +module.exports = require('./dist/shared/lib/runtime-config.shared-runtime') diff --git a/packages/next/package.json b/packages/next/package.json index bebc8aa2b209b..75b2e78962ffc 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -83,6 +83,7 @@ }, "taskr": { "requires": [ + "./taskfile-webpack.js", "./taskfile-ncc.js", "./taskfile-swc.js", "./taskfile-watch.js" @@ -299,6 +300,7 @@ "tar": "6.1.15", "taskr": "1.1.0", "terser": "5.14.1", + "terser-webpack-plugin": "5.3.9", "text-table": "0.2.0", "timers-browserify": "2.0.12", "tty-browserify": "0.0.1", diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 8ad7407b4be45..b0e0870ca1392 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -143,8 +143,13 @@ import { createClientRouterFilter } from '../lib/create-client-router-filter' import { createValidFileMatcher } from '../server/lib/find-page-file' import { startTypeChecking } from './type-check' import { generateInterceptionRoutesRewrites } from '../lib/generate-interception-routes-rewrites' + import { buildDataRoute } from '../server/lib/router-utils/build-data-route' -import { baseOverrides, experimentalOverrides } from '../server/require-hook' +import { + baseOverrides, + defaultOverrides, + experimentalOverrides, +} from '../server/require-hook' import { initialize } from '../server/lib/incremental-cache-server' import { nodeFs } from '../server/lib/node-fs-methods' @@ -1243,6 +1248,7 @@ export default async function build( forkOptions: { env: { ...process.env, + __NEXT_PRIVATE_RENDER_RUNTIME: type, __NEXT_INCREMENTAL_CACHE_IPC_PORT: ipcPort + '', __NEXT_INCREMENTAL_CACHE_IPC_KEY: ipcValidationKey, __NEXT_PRIVATE_PREBUNDLED_REACT: @@ -2084,6 +2090,25 @@ export default async function build( ...Object.values(experimentalOverrides).map((override) => require.resolve(override) ), + ...(config.experimental.turbotrace + ? [] + : Object.keys(defaultOverrides).map((value) => + require.resolve(value, { + paths: [require.resolve('next/dist/server/require-hook')], + }) + )), + require.resolve( + 'next/dist/compiled/next-server/app-page.runtime.prod' + ), + require.resolve( + 'next/dist/compiled/next-server/app-route.runtime.prod' + ), + require.resolve( + 'next/dist/compiled/next-server/pages.runtime.prod' + ), + require.resolve( + 'next/dist/compiled/next-server/pages-api.runtime.prod' + ), ] // ensure we trace any dependencies needed for custom @@ -2109,10 +2134,7 @@ export default async function build( const minimalServerEntries = [ ...sharedEntriesSet, require.resolve( - 'next/dist/compiled/minimal-next-server/next-server-cached.js' - ), - require.resolve( - 'next/dist/compiled/minimal-next-server/next-server.js' + 'next/dist/compiled/next-server/server.runtime.prod' ), ].filter(Boolean) diff --git a/packages/next/src/build/templates/app-page.ts b/packages/next/src/build/templates/app-page.ts index c75509904c3a8..f0d2ab692e2aa 100644 --- a/packages/next/src/build/templates/app-page.ts +++ b/packages/next/src/build/templates/app-page.ts @@ -1,7 +1,7 @@ import type { LoaderTree } from '../../server/lib/app-dir-module' // @ts-ignore this need to be imported from next/dist to be external -import * as module from 'next/dist/server/future/route-modules/app-page/module' +import * as module from 'next/dist/server/future/route-modules/app-page/module.compiled' import { RouteKind } from '../../server/future/route-kind' const AppPageRouteModule = diff --git a/packages/next/src/build/templates/app-route.ts b/packages/next/src/build/templates/app-route.ts index 50a8b6165a747..b4b8e5b0fe6cd 100644 --- a/packages/next/src/build/templates/app-route.ts +++ b/packages/next/src/build/templates/app-route.ts @@ -1,7 +1,8 @@ import '../../server/node-polyfill-headers' // @ts-ignore this need to be imported from next/dist to be external -import * as module from 'next/dist/server/future/route-modules/app-route/module' +import * as module from 'next/dist/server/future/route-modules/app-route/module.compiled' + import type { AppRouteRouteModuleOptions } from '../../server/future/route-modules/app-route/module' import { RouteKind } from '../../server/future/route-kind' diff --git a/packages/next/src/build/templates/pages-api.ts b/packages/next/src/build/templates/pages-api.ts index a48822f9ed75a..eaeec836cb61e 100644 --- a/packages/next/src/build/templates/pages-api.ts +++ b/packages/next/src/build/templates/pages-api.ts @@ -1,5 +1,6 @@ // @ts-ignore this need to be imported from next/dist to be external -import * as module from 'next/dist/server/future/route-modules/pages-api/module' +import * as module from 'next/dist/server/future/route-modules/pages-api/module.compiled' + import { RouteKind } from '../../server/future/route-kind' import { hoist } from './helpers' diff --git a/packages/next/src/build/templates/pages.ts b/packages/next/src/build/templates/pages.ts index 3f3527e6650d6..b5def5d13c552 100644 --- a/packages/next/src/build/templates/pages.ts +++ b/packages/next/src/build/templates/pages.ts @@ -1,5 +1,5 @@ // @ts-ignore this need to be imported from next/dist to be external -import * as module from 'next/dist/server/future/route-modules/pages/module' +import * as module from 'next/dist/server/future/route-modules/pages/module.compiled' import { RouteKind } from '../../server/future/route-kind' import { hoist } from './helpers' diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index 867429c2ba901..423beb27f6dca 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -14,7 +14,7 @@ import type { EdgeFunctionDefinition, MiddlewareManifest, } from './webpack/plugins/middleware-plugin' -import type { StaticGenerationAsyncStorage } from '../client/components/static-generation-async-storage' +import type { StaticGenerationAsyncStorage } from '../client/components/static-generation-async-storage.external' import '../server/require-hook' import '../server/node-polyfill-fetch' @@ -65,7 +65,9 @@ import { nodeFs } from '../server/lib/node-fs-methods' import * as ciEnvironment from '../telemetry/ci-info' import { normalizeAppPath } from '../shared/lib/router/utils/app-paths' import { denormalizeAppPagePath } from '../shared/lib/page-path/denormalize-app-path' -import { AppRouteRouteModule } from '../server/future/route-modules/app-route/module' +// import { AppRouteRouteModule } from '../server/future/route-modules/app-route/module' +const { AppRouteRouteModule } = + require('../server/future/route-modules/app-route/module.compiled') as typeof import('../server/future/route-modules/app-route/module') export type ROUTER_TYPE = 'pages' | 'app' @@ -1389,7 +1391,9 @@ export async function isPageStatic({ const isPageStaticSpan = trace('is-page-static-utils', parentId) return isPageStaticSpan .traceAsyncFn(async () => { - require('../shared/lib/runtime-config').setConfig(runtimeEnvConfig) + require('../shared/lib/runtime-config.shared-runtime').setConfig( + runtimeEnvConfig + ) setHttpClientAndAgentOptions({ httpAgentOptions, }) @@ -1673,7 +1677,9 @@ export async function hasCustomGetInitialProps( runtimeEnvConfig: any, checkingApp: boolean ): Promise { - require('../shared/lib/runtime-config').setConfig(runtimeEnvConfig) + require('../shared/lib/runtime-config.shared-runtime').setConfig( + runtimeEnvConfig + ) const components = await loadComponents({ distDir, @@ -1696,7 +1702,9 @@ export async function getDefinedNamedExports( distDir: string, runtimeEnvConfig: any ): Promise> { - require('../shared/lib/runtime-config').setConfig(runtimeEnvConfig) + require('../shared/lib/runtime-config.shared-runtime').setConfig( + runtimeEnvConfig + ) const components = await loadComponents({ distDir, pathname: page, diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index f1f053d05eb9b..d1b7487618fe2 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -103,6 +103,19 @@ const reactPackagesRegex = /^(react|react-dom|react-server-dom-webpack)($|\/)/ const asyncStoragesRegex = /next[\\/]dist[\\/](esm[\\/])?client[\\/]components[\\/](static-generation-async-storage|action-async-storage|request-async-storage)/ +const pathSeparators = '[/\\\\]' +const optionalEsmPart = `((${pathSeparators}esm)?${pathSeparators})` +const sharedRuntimeFileEnd = '(\\.shared-runtime(\\.js)?)$' +const externalFileEnd = '(\\.external(\\.js)?)$' +const nextDist = `next${pathSeparators}dist` + +const sharedRuntimePattern = new RegExp( + `${nextDist}${optionalEsmPart}.*${sharedRuntimeFileEnd}` +) +const externalPattern = new RegExp( + `${nextDist}${optionalEsmPart}.*${externalFileEnd}` +) + // exports. const edgeConditionNames = [ 'edge-light', @@ -1011,7 +1024,7 @@ export default async function getBaseWebpackConfig( const customRootAliases: { [key: string]: string[] } = {} if (dev) { - const nextDist = 'next/dist/' + (isEdgeServer ? 'esm/' : '') + const nextDistPath = 'next/dist/' + (isEdgeServer ? 'esm/' : '') customAppAliases[`${PAGES_DIR_ALIAS}/_app`] = [ ...(pagesDir ? pageExtensions.reduce((prev, ext) => { @@ -1019,7 +1032,7 @@ export default async function getBaseWebpackConfig( return prev }, [] as string[]) : []), - `${nextDist}pages/_app.js`, + `${nextDistPath}pages/_app.js`, ] customAppAliases[`${PAGES_DIR_ALIAS}/_error`] = [ ...(pagesDir @@ -1028,7 +1041,7 @@ export default async function getBaseWebpackConfig( return prev }, [] as string[]) : []), - `${nextDist}pages/_error.js`, + `${nextDistPath}pages/_error.js`, ] customDocumentAliases[`${PAGES_DIR_ALIAS}/_document`] = [ ...(pagesDir @@ -1037,7 +1050,7 @@ export default async function getBaseWebpackConfig( return prev }, [] as string[]) : []), - `${nextDist}pages/_document.js`, + `${nextDistPath}pages/_document.js`, ] } @@ -1311,6 +1324,7 @@ export default async function getBaseWebpackConfig( WEBPACK_LAYERS.serverSideRendering, WEBPACK_LAYERS.appPagesBrowser, WEBPACK_LAYERS.actionBrowser, + WEBPACK_LAYERS.appRouteHandler, ].includes(layer!) if ( @@ -1367,7 +1381,7 @@ export default async function getBaseWebpackConfig( } const notExternalModules = - /^(?:private-next-pages\/|next\/(?:dist\/pages\/|(?:app|document|link|image|legacy\/image|constants|dynamic|script|navigation|headers)$)|string-hash|private-next-rsc-action-validate|private-next-rsc-action-client-wrapper|private-next-rsc-action-proxy$)/ + /^(?:private-next-pages\/|next\/(?:dist\/pages\/|(?:app|document|link|image|legacy\/image|constants|dynamic|script|navigation|headers|router)$)|string-hash|private-next-rsc-action-validate|private-next-rsc-action-client-wrapper|private-next-rsc-action-proxy$)/ if (notExternalModules.test(request)) { return } @@ -1390,41 +1404,59 @@ export default async function getBaseWebpackConfig( // Also disable esm request when appDir is enabled const isEsmRequested = dependencyType === 'esm' + /** + * @param localRes the full path to the file + * @returns the externalized path + * @description returns an externalized path if the file is a Next.js file and ends with either `.shared-runtime.js` or `.external.js` + * This is used to ensure that files used across the rendering runtime(s) and the user code are one and the same. The logic in this function + * will rewrite the require to the correct bundle location depending on the layer at which the file is being used. + */ const isLocalCallback = (localRes: string) => { - // Makes sure dist/shared and dist/server are not bundled - // we need to process shared `router/router`, `head` and `dynamic`, - // so that the DefinePlugin can inject process.env values. - - // Treat next internals as non-external for server layer - if (isWebpackServerLayer(layer)) { - return + const isSharedRuntime = sharedRuntimePattern.test(localRes) + const isExternal = externalPattern.test(localRes) + + // if the file ends with .external, we need to make it a commonjs require in all cases + // this is used mainly to share the async local storage across the routing, rendering and user layers. + if (isExternal) { + // it's important we return the path that starts with `next/dist/` here instead of the absolute path + // otherwise NFT will get tripped up + return `commonjs ${localRes.replace(/.*?next[/\\]dist/, 'next/dist')}` } + // if the file ends with .shared-runtime, we need to make it point to the correct bundle depending on the layer + // this is because each shared-runtime files are unique per bundle, so if you use app-router context in pages, + // it'll be a different instance than the one used in the app-router runtime. + if (isSharedRuntime) { + if (dev) { + return `commonjs ${localRes}` + } - const isNextExternal = - /next[/\\]dist[/\\](esm[\\/])?(shared|server)[/\\](?!lib[/\\](router[/\\]router|dynamic|app-dynamic|image-external|lazy-dynamic|head[^-]))/.test( - localRes - ) || - // There's no need to bundle the dev overlay - (process.env.NODE_ENV === 'development' && - /next[/\\]dist[/\\](esm[/\\])?client[/\\]components[/\\]react-dev-overlay[/\\]/.test( - localRes - )) - - if (isNextExternal) { - // Generate Next.js external import - const externalRequest = path.posix.join( - 'next', - 'dist', - path - .relative( - // Root of Next.js package: - path.join(__dirname, '..'), - localRes - ) - // Windows path normalization - .replace(/\\/g, '/') + const name = path.parse(localRes).name.replace('.shared-runtime', '') + + const camelCaseName = name.replace(/-([a-z])/g, (_, w) => + w.toUpperCase() ) - return `commonjs ${externalRequest}` + + // there's no externals for API routes but if need be, they'll need to be added here and have + // their own layer + const runtime = + layer === 'app-route-handler' + ? 'app-route' + : isAppLayer + ? 'app-page' + : 'pages' + return [ + 'commonjs ' + + path.posix.join( + 'next', + 'dist', + 'compiled', + 'next-server', + `${runtime}.runtime.${dev ? 'dev' : 'prod'}` + ), + 'default', + 'sharedModules', + camelCaseName, + ] } } @@ -1445,6 +1477,10 @@ export default async function getBaseWebpackConfig( return } + if (/^next\/dist\/compiled\/next-server/.test(request)) { + return `commonjs ${request}` + } + if ( /^next\/dist\/shared\/(?!lib\/router\/router)/.test(request) || /^next\/dist\/compiled\/.*\.c?js$/.test(request) @@ -2031,6 +2067,14 @@ export default async function getBaseWebpackConfig( }, ...(hasAppDir ? [ + { + layer: WEBPACK_LAYERS.appRouteHandler, + test: new RegExp( + `private-next-app-dir\\/.*\\/route\\.(${pageExtensions.join( + '|' + )})$` + ), + }, { // Make sure that AsyncLocalStorage module instance is shared between server and client // layers. @@ -2239,7 +2283,7 @@ export default async function getBaseWebpackConfig( WEBPACK_LAYERS.appPagesBrowser, ], }, - exclude: [asyncStoragesRegex, codeCondition.exclude], + exclude: [codeCondition.exclude], use: [ ...(dev && isClient ? [ diff --git a/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts b/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts index 2be12daecdbc6..c2b1089df5b3f 100644 --- a/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts +++ b/packages/next/src/build/webpack/plugins/next-types-plugin/index.ts @@ -442,7 +442,7 @@ declare module 'next/link' { declare module 'next/navigation' { export * from 'next/dist/client/components/navigation.js' - import type { NavigateOptions, AppRouterInstance as OriginalAppRouterInstance } from 'next/dist/shared/lib/app-router-context.js' + import type { NavigateOptions, AppRouterInstance as OriginalAppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime.js' interface AppRouterInstance extends OriginalAppRouterInstance { /** * Navigate to the provided href. @@ -575,8 +575,11 @@ export class NextTypesPlugin { } return } - - if (mod.layer !== WEBPACK_LAYERS.reactServerComponents) return + if ( + mod.layer !== WEBPACK_LAYERS.reactServerComponents && + mod.layer !== WEBPACK_LAYERS.appRouteHandler + ) + return const IS_LAYOUT = /[/\\]layout\.[^./\\]+$/.test(mod.resource) const IS_PAGE = !IS_LAYOUT && /[/\\]page\.[^.]+$/.test(mod.resource) diff --git a/packages/next/src/client/app-index.tsx b/packages/next/src/client/app-index.tsx index 8f82d244837e6..47c0bd13f369e 100644 --- a/packages/next/src/client/app-index.tsx +++ b/packages/next/src/client/app-index.tsx @@ -7,8 +7,8 @@ import React, { use } from 'react' // eslint-disable-next-line import/no-extraneous-dependencies import { createFromReadableStream } from 'react-server-dom-webpack/client' -import { HeadManagerContext } from '../shared/lib/head-manager-context' -import { GlobalLayoutRouterContext } from '../shared/lib/app-router-context' +import { HeadManagerContext } from '../shared/lib/head-manager-context.shared-runtime' +import { GlobalLayoutRouterContext } from '../shared/lib/app-router-context.shared-runtime' import onRecoverableError from './on-recoverable-error' import { callServer } from './app-call-server' import { isNextRouterError } from './components/is-next-router-error' diff --git a/packages/next/src/client/compat/router.ts b/packages/next/src/client/compat/router.ts index 58b1b9f02ed05..e9143c4117bd7 100644 --- a/packages/next/src/client/compat/router.ts +++ b/packages/next/src/client/compat/router.ts @@ -1,5 +1,5 @@ import { useContext } from 'react' -import { RouterContext } from '../../shared/lib/router-context' +import { RouterContext } from '../../shared/lib/router-context.shared-runtime' import { NextRouter } from '../router' /** diff --git a/packages/next/src/client/components/action-async-storage.ts b/packages/next/src/client/components/action-async-storage.external.ts similarity index 100% rename from packages/next/src/client/components/action-async-storage.ts rename to packages/next/src/client/components/action-async-storage.external.ts diff --git a/packages/next/src/client/components/app-router.tsx b/packages/next/src/client/components/app-router.tsx index 2382d6a83925e..2c3c541e02b8e 100644 --- a/packages/next/src/client/components/app-router.tsx +++ b/packages/next/src/client/components/app-router.tsx @@ -14,11 +14,11 @@ import { LayoutRouterContext, GlobalLayoutRouterContext, CacheStates, -} from '../../shared/lib/app-router-context' +} from '../../shared/lib/app-router-context.shared-runtime' import type { CacheNode, AppRouterInstance, -} from '../../shared/lib/app-router-context' +} from '../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState, FlightData, @@ -44,7 +44,7 @@ import { createHrefFromUrl } from './router-reducer/create-href-from-url' import { SearchParamsContext, PathnameContext, -} from '../../shared/lib/hooks-client-context' +} from '../../shared/lib/hooks-client-context.shared-runtime' import { useReducerWithReduxDevtools } from './use-reducer-with-devtools' import { ErrorBoundary } from './error-boundary' import { diff --git a/packages/next/src/client/components/bailout-to-client-rendering.ts b/packages/next/src/client/components/bailout-to-client-rendering.ts index 76356e05304a8..799398b5f300c 100644 --- a/packages/next/src/client/components/bailout-to-client-rendering.ts +++ b/packages/next/src/client/components/bailout-to-client-rendering.ts @@ -1,5 +1,5 @@ import { suspense } from '../../shared/lib/lazy-dynamic/dynamic-no-ssr' -import { staticGenerationAsyncStorage } from './static-generation-async-storage' +import { staticGenerationAsyncStorage } from './static-generation-async-storage.external' export function bailoutToClientRendering(): boolean | never { const staticGenerationStore = staticGenerationAsyncStorage.getStore() diff --git a/packages/next/src/client/components/headers.ts b/packages/next/src/client/components/headers.ts index d090264391e7c..a0a27a184cbfe 100644 --- a/packages/next/src/client/components/headers.ts +++ b/packages/next/src/client/components/headers.ts @@ -4,8 +4,8 @@ import { } from '../../server/web/spec-extension/adapters/request-cookies' import { HeadersAdapter } from '../../server/web/spec-extension/adapters/headers' import { RequestCookies } from '../../server/web/spec-extension/cookies' -import { requestAsyncStorage } from './request-async-storage' -import { actionAsyncStorage } from './action-async-storage' +import { requestAsyncStorage } from './request-async-storage.external' +import { actionAsyncStorage } from './action-async-storage.external' import { staticGenerationBailout } from './static-generation-bailout' import { DraftMode } from './draft-mode' @@ -17,7 +17,6 @@ export function headers() { ) { return HeadersAdapter.seal(new Headers({})) } - const requestStore = requestAsyncStorage.getStore() if (!requestStore) { throw new Error( diff --git a/packages/next/src/client/components/layout-router.tsx b/packages/next/src/client/components/layout-router.tsx index 3e410a93fb0ca..1f0ffff7e2de8 100644 --- a/packages/next/src/client/components/layout-router.tsx +++ b/packages/next/src/client/components/layout-router.tsx @@ -1,6 +1,6 @@ 'use client' -import type { ChildSegmentMap } from '../../shared/lib/app-router-context' +import type { ChildSegmentMap } from '../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState, FlightSegmentPath, @@ -17,7 +17,7 @@ import { LayoutRouterContext, GlobalLayoutRouterContext, TemplateContext, -} from '../../shared/lib/app-router-context' +} from '../../shared/lib/app-router-context.shared-runtime' import { fetchServerResponse } from './router-reducer/fetch-server-response' import { createInfinitePromise } from './infinite-promise' import { ErrorBoundary } from './error-boundary' diff --git a/packages/next/src/client/components/navigation.ts b/packages/next/src/client/components/navigation.ts index bf6a56100080d..b3d69dcb065e8 100644 --- a/packages/next/src/client/components/navigation.ts +++ b/packages/next/src/client/components/navigation.ts @@ -4,11 +4,11 @@ import { AppRouterContext, GlobalLayoutRouterContext, LayoutRouterContext, -} from '../../shared/lib/app-router-context' +} from '../../shared/lib/app-router-context.shared-runtime' import { SearchParamsContext, PathnameContext, -} from '../../shared/lib/hooks-client-context' +} from '../../shared/lib/hooks-client-context.shared-runtime' import { clientHookInServerComponentError } from './client-hook-in-server-component-error' import { getSegmentValue } from './router-reducer/reducers/get-segment-value' @@ -111,12 +111,12 @@ export function usePathname(): string { export { ServerInsertedHTMLContext, useServerInsertedHTML, -} from '../../shared/lib/server-inserted-html' +} from '../../shared/lib/server-inserted-html.shared-runtime' /** * Get the router methods. For example router.push('/dashboard') */ -export function useRouter(): import('../../shared/lib/app-router-context').AppRouterInstance { +export function useRouter(): import('../../shared/lib/app-router-context.shared-runtime').AppRouterInstance { clientHookInServerComponentError('useRouter') const router = useContext(AppRouterContext) if (router === null) { diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-websocket.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-websocket.ts index 4d92a279c3ed5..d37fce9851e91 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-websocket.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/use-websocket.ts @@ -1,5 +1,5 @@ import { useCallback, useContext, useEffect, useRef } from 'react' -import { GlobalLayoutRouterContext } from '../../../../../shared/lib/app-router-context' +import { GlobalLayoutRouterContext } from '../../../../../shared/lib/app-router-context.shared-runtime' import { getSocketUrl } from './get-socket-url' export function useWebsocket(assetPrefix: string) { diff --git a/packages/next/src/client/components/redirect-boundary.tsx b/packages/next/src/client/components/redirect-boundary.tsx index 23e5493ae83fb..8d407fd6e9d6e 100644 --- a/packages/next/src/client/components/redirect-boundary.tsx +++ b/packages/next/src/client/components/redirect-boundary.tsx @@ -1,6 +1,6 @@ 'use client' import React, { useEffect } from 'react' -import { AppRouterInstance } from '../../shared/lib/app-router-context' +import { AppRouterInstance } from '../../shared/lib/app-router-context.shared-runtime' import { useRouter } from './navigation' import { RedirectType, diff --git a/packages/next/src/client/components/redirect.ts b/packages/next/src/client/components/redirect.ts index 10e72bc1ccbef..b9a2cfebd883f 100644 --- a/packages/next/src/client/components/redirect.ts +++ b/packages/next/src/client/components/redirect.ts @@ -1,4 +1,4 @@ -import { requestAsyncStorage } from './request-async-storage' +import { requestAsyncStorage } from './request-async-storage.external' import type { ResponseCookies } from '../../server/web/spec-extension/cookies' const REDIRECT_ERROR_CODE = 'NEXT_REDIRECT' diff --git a/packages/next/src/client/components/render-from-template-context.tsx b/packages/next/src/client/components/render-from-template-context.tsx index be486842c4f33..c1755cc5056bf 100644 --- a/packages/next/src/client/components/render-from-template-context.tsx +++ b/packages/next/src/client/components/render-from-template-context.tsx @@ -1,7 +1,7 @@ 'use client' import React, { useContext } from 'react' -import { TemplateContext } from '../../shared/lib/app-router-context' +import { TemplateContext } from '../../shared/lib/app-router-context.shared-runtime' export default function RenderFromTemplateContext(): JSX.Element { const children = useContext(TemplateContext) diff --git a/packages/next/src/client/components/request-async-storage.ts b/packages/next/src/client/components/request-async-storage.external.ts similarity index 100% rename from packages/next/src/client/components/request-async-storage.ts rename to packages/next/src/client/components/request-async-storage.external.ts diff --git a/packages/next/src/client/components/router-reducer/apply-flight-data.ts b/packages/next/src/client/components/router-reducer/apply-flight-data.ts index e7a2f11a84f48..003d0a5cde9e4 100644 --- a/packages/next/src/client/components/router-reducer/apply-flight-data.ts +++ b/packages/next/src/client/components/router-reducer/apply-flight-data.ts @@ -1,4 +1,7 @@ -import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' +import { + CacheNode, + CacheStates, +} from '../../../shared/lib/app-router-context.shared-runtime' import { FlightDataPath } from '../../../server/app-render/types' import { fillLazyItemsTillLeafWithHead } from './fill-lazy-items-till-leaf-with-head' import { fillCacheWithNewSubTreeData } from './fill-cache-with-new-subtree-data' diff --git a/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx b/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx index a6052636ef256..414b553c63249 100644 --- a/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx +++ b/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx @@ -1,6 +1,9 @@ import React from 'react' import type { FlightRouterState } from '../../../server/app-render/types' -import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' +import { + CacheNode, + CacheStates, +} from '../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from './create-initial-router-state' const buildId = 'development' diff --git a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts index 7f7cca2003b0b..94fdabb9b577a 100644 --- a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts +++ b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts @@ -1,8 +1,8 @@ import type { ReactNode } from 'react' -import type { CacheNode } from '../../../shared/lib/app-router-context' +import type { CacheNode } from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState } from '../../../server/app-render/types' -import { CacheStates } from '../../../shared/lib/app-router-context' +import { CacheStates } from '../../../shared/lib/app-router-context.shared-runtime' import { createHrefFromUrl } from './create-href-from-url' import { fillLazyItemsTillLeafWithHead } from './fill-lazy-items-till-leaf-with-head' import { extractPathFromFlightRouterState } from './compute-changed-path' diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx index 28f8c3412ab3a..648069ea76986 100644 --- a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx +++ b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx @@ -1,7 +1,10 @@ import React from 'react' import { fetchServerResponse } from './fetch-server-response' import { fillCacheWithDataProperty } from './fill-cache-with-data-property' -import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' +import { + CacheStates, + CacheNode, +} from '../../../shared/lib/app-router-context.shared-runtime' describe('fillCacheWithDataProperty', () => { it('should add data property', () => { const fetchServerResponseMock: jest.Mock< diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts index 81df295dba302..42df61a952af5 100644 --- a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts +++ b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts @@ -1,5 +1,8 @@ import { FlightSegmentPath } from '../../../server/app-render/types' -import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' +import { + CacheNode, + CacheStates, +} from '../../../shared/lib/app-router-context.shared-runtime' import { createRouterCacheKey } from './create-router-cache-key' import { fetchServerResponse } from './fetch-server-response' diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx index 187f86a478751..ac888a3ede0ff 100644 --- a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx +++ b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx @@ -1,6 +1,9 @@ import React from 'react' import { fillCacheWithNewSubTreeData } from './fill-cache-with-new-subtree-data' -import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' +import { + CacheStates, + CacheNode, +} from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightData } from '../../../server/app-render/types' const getFlightData = (): FlightData => { diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts index 5d48eaee9ef9f..7e9a93699fb65 100644 --- a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts +++ b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts @@ -1,4 +1,7 @@ -import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' +import { + CacheNode, + CacheStates, +} from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightDataPath } from '../../../server/app-render/types' import { invalidateCacheByRouterState } from './invalidate-cache-by-router-state' import { fillLazyItemsTillLeafWithHead } from './fill-lazy-items-till-leaf-with-head' diff --git a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx index 606440a96f9c9..1edbeffd7b3e9 100644 --- a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx +++ b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx @@ -1,6 +1,9 @@ import React from 'react' import { fillLazyItemsTillLeafWithHead } from './fill-lazy-items-till-leaf-with-head' -import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' +import { + CacheStates, + CacheNode, +} from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightData } from '../../../server/app-render/types' const getFlightData = (): FlightData => { diff --git a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts index c5ddedd52351e..f558edfab2f1e 100644 --- a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts +++ b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts @@ -1,4 +1,7 @@ -import { CacheNode, CacheStates } from '../../../shared/lib/app-router-context' +import { + CacheNode, + CacheStates, +} from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState } from '../../../server/app-render/types' import { createRouterCacheKey } from './create-router-cache-key' diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx index 915f09cae0cae..8c23c47d42d74 100644 --- a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx +++ b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx @@ -1,7 +1,10 @@ import React from 'react' import type { FlightData } from '../../../server/app-render/types' import { invalidateCacheBelowFlightSegmentPath } from './invalidate-cache-below-flight-segmentpath' -import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' +import { + CacheStates, + CacheNode, +} from '../../../shared/lib/app-router-context.shared-runtime' import { fillCacheWithNewSubTreeData } from './fill-cache-with-new-subtree-data' const getFlightData = (): FlightData => { diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts index ac343f8d79679..d637d850b145a 100644 --- a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts +++ b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts @@ -1,4 +1,4 @@ -import type { CacheNode } from '../../../shared/lib/app-router-context' +import type { CacheNode } from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightSegmentPath } from '../../../server/app-render/types' import { createRouterCacheKey } from './create-router-cache-key' diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx b/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx index 65ce9e42c05ee..bdd819b0614d9 100644 --- a/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx +++ b/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx @@ -1,6 +1,9 @@ import React from 'react' import { invalidateCacheByRouterState } from './invalidate-cache-by-router-state' -import { CacheStates, CacheNode } from '../../../shared/lib/app-router-context' +import { + CacheStates, + CacheNode, +} from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState } from '../../../server/app-render/types' describe('invalidateCacheByRouterState', () => { diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.ts b/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.ts index 820e5909bf031..1ec39ae9e35fd 100644 --- a/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.ts +++ b/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.ts @@ -1,4 +1,4 @@ -import type { CacheNode } from '../../../shared/lib/app-router-context' +import type { CacheNode } from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState } from '../../../server/app-render/types' import { createRouterCacheKey } from './create-router-cache-key' diff --git a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx b/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx index 807374c855577..2d4cdef348b1e 100644 --- a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx @@ -3,7 +3,7 @@ import type { FlightRouterState } from '../../../../server/app-render/types' import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { findHeadInCache } from './find-head-in-cache' describe('findHeadInCache', () => { diff --git a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.ts b/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.ts index f4d5e768b9808..08dcefc65f2ce 100644 --- a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.ts +++ b/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.ts @@ -1,5 +1,5 @@ import type { FlightRouterState } from '../../../../server/app-render/types' -import type { CacheNode } from '../../../../shared/lib/app-router-context' +import type { CacheNode } from '../../../../shared/lib/app-router-context.shared-runtime' import { createRouterCacheKey } from '../create-router-cache-key' export function findHeadInCache( diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx index 6ff039d14cf54..4ed01fed08c83 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx @@ -79,7 +79,7 @@ import { FlightRouterState } from '../../../../server/app-render/types' import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from '../create-initial-router-state' import { NavigateAction, diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts index fe8a4a24c4b8b..e47c42b2aa60a 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts @@ -1,7 +1,7 @@ import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState, FlightSegmentPath, diff --git a/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx index 8055123367a94..bbbee6ff5f2a4 100644 --- a/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx @@ -36,7 +36,7 @@ import { FlightRouterState } from '../../../../server/app-render/types' import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from '../create-initial-router-state' import { PrefetchAction, diff --git a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx index 1ffbb376e2f96..9ed54e8994d2e 100644 --- a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx @@ -46,7 +46,7 @@ import { FlightRouterState } from '../../../../server/app-render/types' import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from '../create-initial-router-state' import { RefreshAction, ACTION_REFRESH } from '../router-reducer-types' import { refreshReducer } from './refresh-reducer' diff --git a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts index cd87ef3802b00..bd6dfc4ef9047 100644 --- a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts @@ -11,7 +11,7 @@ import { } from '../router-reducer-types' import { handleExternalUrl } from './navigate-reducer' import { handleMutable } from '../handle-mutable' -import { CacheStates } from '../../../../shared/lib/app-router-context' +import { CacheStates } from '../../../../shared/lib/app-router-context.shared-runtime' import { fillLazyItemsTillLeafWithHead } from '../fill-lazy-items-till-leaf-with-head' export function refreshReducer( diff --git a/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx index b11f39b141ccf..36c978926517f 100644 --- a/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx @@ -3,7 +3,7 @@ import type { FlightRouterState } from '../../../../server/app-render/types' import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from '../create-initial-router-state' import { RestoreAction, ACTION_RESTORE } from '../router-reducer-types' import { restoreReducer } from './restore-reducer' diff --git a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts index 3b8fa6acb4013..0c6caaba746ca 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts @@ -27,7 +27,7 @@ import { createHrefFromUrl } from '../create-href-from-url' import { handleExternalUrl } from './navigate-reducer' import { applyRouterStatePatchToTree } from '../apply-router-state-patch-to-tree' import { isNavigatingToNewRootLayout } from '../is-navigating-to-new-root-layout' -import { CacheStates } from '../../../../shared/lib/app-router-context' +import { CacheStates } from '../../../../shared/lib/app-router-context.shared-runtime' import { handleMutable } from '../handle-mutable' import { fillLazyItemsTillLeafWithHead } from '../fill-lazy-items-till-leaf-with-head' diff --git a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx index 37ef29296c891..0540c02079cb1 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx +++ b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx @@ -41,7 +41,7 @@ jest.mock('../fetch-server-response', () => { import { CacheNode, CacheStates, -} from '../../../../shared/lib/app-router-context' +} from '../../../../shared/lib/app-router-context.shared-runtime' import { createInitialRouterState } from '../create-initial-router-state' import { ServerPatchAction, diff --git a/packages/next/src/client/components/router-reducer/router-reducer-types.ts b/packages/next/src/client/components/router-reducer/router-reducer-types.ts index 48840a29db6db..defbb657c7c42 100644 --- a/packages/next/src/client/components/router-reducer/router-reducer-types.ts +++ b/packages/next/src/client/components/router-reducer/router-reducer-types.ts @@ -1,4 +1,4 @@ -import type { CacheNode } from '../../../shared/lib/app-router-context' +import type { CacheNode } from '../../../shared/lib/app-router-context.shared-runtime' import type { FlightRouterState, FlightData, diff --git a/packages/next/src/client/components/static-generation-async-storage.ts b/packages/next/src/client/components/static-generation-async-storage.external.ts similarity index 100% rename from packages/next/src/client/components/static-generation-async-storage.ts rename to packages/next/src/client/components/static-generation-async-storage.external.ts diff --git a/packages/next/src/client/components/static-generation-bailout.ts b/packages/next/src/client/components/static-generation-bailout.ts index c5072218f035c..4d35150664251 100644 --- a/packages/next/src/client/components/static-generation-bailout.ts +++ b/packages/next/src/client/components/static-generation-bailout.ts @@ -1,5 +1,5 @@ import { DynamicServerError } from './hooks-server-context' -import { staticGenerationAsyncStorage } from './static-generation-async-storage' +import { staticGenerationAsyncStorage } from './static-generation-async-storage.external' class StaticGenBailoutError extends Error { code = 'NEXT_STATIC_GEN_BAILOUT' diff --git a/packages/next/src/client/image-component.tsx b/packages/next/src/client/image-component.tsx index 3f2183c004b10..321b07ecd0a5f 100644 --- a/packages/next/src/client/image-component.tsx +++ b/packages/next/src/client/image-component.tsx @@ -25,9 +25,9 @@ import type { ImageLoaderProps, } from '../shared/lib/image-config' import { imageConfigDefault } from '../shared/lib/image-config' -import { ImageConfigContext } from '../shared/lib/image-config-context' +import { ImageConfigContext } from '../shared/lib/image-config-context.shared-runtime' import { warnOnce } from '../shared/lib/utils/warn-once' -import { RouterContext } from '../shared/lib/router-context' +import { RouterContext } from '../shared/lib/router-context.shared-runtime' // @ts-ignore - This is replaced by webpack alias import defaultLoader from 'next/dist/shared/lib/image-loader' diff --git a/packages/next/src/client/index.tsx b/packages/next/src/client/index.tsx index f7c163f020d31..0f8a95c93746e 100644 --- a/packages/next/src/client/index.tsx +++ b/packages/next/src/client/index.tsx @@ -10,16 +10,16 @@ import type { import React from 'react' import ReactDOM from 'react-dom/client' -import { HeadManagerContext } from '../shared/lib/head-manager-context' +import { HeadManagerContext } from '../shared/lib/head-manager-context.shared-runtime' import mitt, { MittEmitter } from '../shared/lib/mitt' -import { RouterContext } from '../shared/lib/router-context' +import { RouterContext } from '../shared/lib/router-context.shared-runtime' import { handleSmoothScroll } from '../shared/lib/router/utils/handle-smooth-scroll' import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic' import { urlQueryToSearchParams, assign, } from '../shared/lib/router/utils/querystring' -import { setConfig } from '../shared/lib/runtime-config' +import { setConfig } from '../shared/lib/runtime-config.shared-runtime' import { getURL, loadGetInitialProps, @@ -34,17 +34,17 @@ import measureWebVitals from './performance-relayer' import { RouteAnnouncer } from './route-announcer' import { createRouter, makePublicRouterInstance } from './router' import { getProperError } from '../lib/is-error' -import { ImageConfigContext } from '../shared/lib/image-config-context' +import { ImageConfigContext } from '../shared/lib/image-config-context.shared-runtime' import { ImageConfigComplete } from '../shared/lib/image-config' import { removeBasePath } from './remove-base-path' import { hasBasePath } from './has-base-path' -import { AppRouterContext } from '../shared/lib/app-router-context' +import { AppRouterContext } from '../shared/lib/app-router-context.shared-runtime' import { adaptForAppRouterInstance, adaptForSearchParams, PathnameContextProviderAdapter, -} from '../shared/lib/router/adapters' -import { SearchParamsContext } from '../shared/lib/hooks-client-context' +} from '../shared/lib/router/adapters.shared-runtime' +import { SearchParamsContext } from '../shared/lib/hooks-client-context.shared-runtime' import onRecoverableError from './on-recoverable-error' import tracer from './tracing/tracer' import reportToSocket from './tracing/report-to-socket' diff --git a/packages/next/src/client/legacy/image.tsx b/packages/next/src/client/legacy/image.tsx index d1456477bacf6..07ec2e217c200 100644 --- a/packages/next/src/client/legacy/image.tsx +++ b/packages/next/src/client/legacy/image.tsx @@ -16,7 +16,7 @@ import { VALID_LOADERS, } from '../../shared/lib/image-config' import { useIntersection } from '../use-intersection' -import { ImageConfigContext } from '../../shared/lib/image-config-context' +import { ImageConfigContext } from '../../shared/lib/image-config-context.shared-runtime' import { warnOnce } from '../../shared/lib/utils/warn-once' import { normalizePathTrailingSlash } from '../normalize-trailing-slash' diff --git a/packages/next/src/client/link.tsx b/packages/next/src/client/link.tsx index 94226c8caa5c0..7a15dee249e26 100644 --- a/packages/next/src/client/link.tsx +++ b/packages/next/src/client/link.tsx @@ -12,12 +12,12 @@ import { isLocalURL } from '../shared/lib/router/utils/is-local-url' import { formatUrl } from '../shared/lib/router/utils/format-url' import { isAbsoluteUrl } from '../shared/lib/utils' import { addLocale } from './add-locale' -import { RouterContext } from '../shared/lib/router-context' +import { RouterContext } from '../shared/lib/router-context.shared-runtime' import { AppRouterContext, AppRouterInstance, PrefetchOptions as AppRouterPrefetchOptions, -} from '../shared/lib/app-router-context' +} from '../shared/lib/app-router-context.shared-runtime' import { useIntersection } from './use-intersection' import { getDomainLocale } from './get-domain-locale' import { addBasePath } from './add-base-path' diff --git a/packages/next/src/client/router.ts b/packages/next/src/client/router.ts index 342ecb623df74..f43126691e5e5 100644 --- a/packages/next/src/client/router.ts +++ b/packages/next/src/client/router.ts @@ -2,7 +2,7 @@ import React from 'react' import Router from '../shared/lib/router/router' import type { NextRouter } from '../shared/lib/router/router' -import { RouterContext } from '../shared/lib/router-context' +import { RouterContext } from '../shared/lib/router-context.shared-runtime' import isError from '../lib/is-error' type SingletonRouterBase = { diff --git a/packages/next/src/client/script.tsx b/packages/next/src/client/script.tsx index a4b2c6dcd3184..f695e691c482d 100644 --- a/packages/next/src/client/script.tsx +++ b/packages/next/src/client/script.tsx @@ -3,7 +3,7 @@ import ReactDOM from 'react-dom' import React, { useEffect, useContext, useRef } from 'react' import { ScriptHTMLAttributes } from 'react' -import { HeadManagerContext } from '../shared/lib/head-manager-context' +import { HeadManagerContext } from '../shared/lib/head-manager-context.shared-runtime' import { DOMAttributeNames } from './head-manager' import { requestIdleCallback } from './request-idle-callback' diff --git a/packages/next/src/export/worker.ts b/packages/next/src/export/worker.ts index 31f58755996a4..b9fb7b9afd60a 100644 --- a/packages/next/src/export/worker.ts +++ b/packages/next/src/export/worker.ts @@ -59,7 +59,7 @@ import { RSC, } from '../client/components/app-router-headers' -const envConfig = require('../shared/lib/runtime-config') +const envConfig = require('../shared/lib/runtime-config.shared-runtime') ;(globalThis as any).__NEXT_DATA__ = { nextExport: true, @@ -307,8 +307,10 @@ export default async function exportPage({ await promises.mkdir(baseDir, { recursive: true }) let renderResult: RenderResult | undefined let curRenderOpts: RenderOpts = {} - const { renderToHTML } = - require('../server/render') as typeof import('../server/render') + const renderToHTML = + require('../server/future/route-modules/pages/module.compiled') + .renderToHTML as typeof import('../server/render').renderToHTML + let renderMethod = renderToHTML let inAmpMode = false, hybridAmp = false @@ -479,7 +481,6 @@ export default async function exportPage({ const module = await RouteModuleLoader.load( filename ) - // Call the handler with the request and context from the module. const response = await module.handle(request, context) @@ -535,8 +536,9 @@ export default async function exportPage({ results.fromBuildExportRevalidate = 0 } } else { - const { renderToHTMLOrFlight } = - require('../server/app-render/app-render') as typeof import('../server/app-render/app-render') + const renderToHTMLOrFlight = + require('../server/future/route-modules/app-page/module.compiled') + .renderToHTMLOrFlight as typeof import('../server/app-render/app-render').renderToHTMLOrFlight try { curRenderOpts.params ||= {} diff --git a/packages/next/src/lib/chalk.ts b/packages/next/src/lib/chalk.ts index 8e40472953f8f..d0939d9148b97 100644 --- a/packages/next/src/lib/chalk.ts +++ b/packages/next/src/lib/chalk.ts @@ -1,6 +1,6 @@ let chalk: typeof import('next/dist/compiled/chalk') -if (process.env.NEXT_RUNTIME === 'edge') { +if (process.env.NEXT_RUNTIME === 'edge' || process.env.NEXT_MINIMAL) { chalk = require('./web/chalk').default } else { chalk = require('next/dist/compiled/chalk') diff --git a/packages/next/src/lib/constants.ts b/packages/next/src/lib/constants.ts index fc94397c6aca7..46f85311a9f0a 100644 --- a/packages/next/src/lib/constants.ts +++ b/packages/next/src/lib/constants.ts @@ -132,6 +132,10 @@ const WEBPACK_LAYERS_NAMES = { * The server bundle layer for metadata routes. */ appMetadataRoute: 'app-metadata-route', + /** + * The layer for the server bundle for App Route handlers. + */ + appRouteHandler: 'app-route-handler', } export const WEBPACK_LAYERS = { @@ -141,6 +145,7 @@ export const WEBPACK_LAYERS = { WEBPACK_LAYERS_NAMES.reactServerComponents, WEBPACK_LAYERS_NAMES.actionBrowser, WEBPACK_LAYERS_NAMES.appMetadataRoute, + WEBPACK_LAYERS_NAMES.appRouteHandler, ], }, } diff --git a/packages/next/src/pages/_document.tsx b/packages/next/src/pages/_document.tsx index a8526011c5d33..f0a29cb15d1fc 100644 --- a/packages/next/src/pages/_document.tsx +++ b/packages/next/src/pages/_document.tsx @@ -17,8 +17,11 @@ import { BuildManifest, getPageFiles } from '../server/get-page-files' import { htmlEscapeJsonString } from '../server/htmlescape' import isError from '../lib/is-error' -import { HtmlContext, useHtmlContext } from '../shared/lib/html-context' -import type { HtmlProps } from '../shared/lib/html-context' +import { + HtmlContext, + useHtmlContext, +} from '../shared/lib/html-context.shared-runtime' +import type { HtmlProps } from '../shared/lib/html-context.shared-runtime' export { DocumentContext, DocumentInitialProps, DocumentProps } diff --git a/packages/next/src/server/app-render/action-handler.ts b/packages/next/src/server/app-render/action-handler.ts index 6b8bc3228a005..9f6059ec48f5a 100644 --- a/packages/next/src/server/app-render/action-handler.ts +++ b/packages/next/src/server/app-render/action-handler.ts @@ -19,10 +19,10 @@ import { isRedirectError, } from '../../client/components/redirect' import RenderResult from '../render-result' -import { StaticGenerationStore } from '../../client/components/static-generation-async-storage' +import { StaticGenerationStore } from '../../client/components/static-generation-async-storage.external' import { FlightRenderResult } from './flight-render-result' import { ActionResult } from './types' -import { ActionAsyncStorage } from '../../client/components/action-async-storage' +import { ActionAsyncStorage } from '../../client/components/action-async-storage.external' import { filterReqHeaders, actionsForbiddenHeaders, @@ -31,7 +31,8 @@ import { appendMutableCookies, getModifiedCookieValues, } from '../web/spec-extension/adapters/request-cookies' -import { RequestStore } from '../../client/components/request-async-storage' + +import { RequestStore } from '../../client/components/request-async-storage.external' import { NEXT_CACHE_REVALIDATED_TAGS_HEADER, NEXT_CACHE_REVALIDATE_TAG_TOKEN_HEADER, diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 30749ac66a83d..1dd2eb8f7c639 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -11,9 +11,9 @@ import type { Segment, } from './types' -import type { StaticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage' +import type { StaticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage.external' import type { StaticGenerationBailout } from '../../client/components/static-generation-bailout' -import type { RequestAsyncStorage } from '../../client/components/request-async-storage' +import type { RequestAsyncStorage } from '../../client/components/request-async-storage.external' import React from 'react' import { createServerComponentRenderer } from './create-server-components-renderer' @@ -286,10 +286,13 @@ export async function renderToHTMLOrFlight( * that we need to resolve the final metadata. */ - const requestId = - process.env.NEXT_RUNTIME === 'edge' - ? crypto.randomUUID() - : require('next/dist/compiled/nanoid').nanoid() + let requestId: string + + if (process.env.NEXT_RUNTIME === 'edge') { + requestId = crypto.randomUUID() + } else { + requestId = require('next/dist/compiled/nanoid').nanoid() + } const LayoutRouter = ComponentMod.LayoutRouter as typeof import('../../client/components/layout-router').default @@ -1392,7 +1395,7 @@ export async function renderToHTMLOrFlight( ) const { HeadManagerContext } = - require('../../shared/lib/head-manager-context') as typeof import('../../shared/lib/head-manager-context') + require('../../shared/lib/head-manager-context.shared-runtime') as typeof import('../../shared/lib/head-manager-context.shared-runtime') // On each render, create a new `ServerInsertedHTML` context to capture // injected nodes from user code (`useServerInsertedHTML`). diff --git a/packages/next/src/server/app-render/entry-base.ts b/packages/next/src/server/app-render/entry-base.ts index 6bc5fd7e7ace1..0cc53e0214d86 100644 --- a/packages/next/src/server/app-render/entry-base.ts +++ b/packages/next/src/server/app-render/entry-base.ts @@ -1,36 +1,26 @@ -const { default: AppRouter } = - require('next/dist/client/components/app-router') as typeof import('../../client/components/app-router') -const { default: LayoutRouter } = - require('next/dist/client/components/layout-router') as typeof import('../../client/components/layout-router') -const { default: RenderFromTemplateContext } = - require('next/dist/client/components/render-from-template-context') as typeof import('../../client/components/render-from-template-context') - -const { staticGenerationAsyncStorage } = - require('next/dist/client/components/static-generation-async-storage') as typeof import('../../client/components/static-generation-async-storage') - -const { requestAsyncStorage } = - require('next/dist/client/components/request-async-storage') as typeof import('../../client/components/request-async-storage') -const { actionAsyncStorage } = - require('next/dist/client/components/action-async-storage') as typeof import('../../client/components/action-async-storage') - -const { staticGenerationBailout } = - require('next/dist/client/components/static-generation-bailout') as typeof import('../../client/components/static-generation-bailout') -const { default: StaticGenerationSearchParamsBailoutProvider } = - require('next/dist/client/components/static-generation-searchparams-bailout-provider') as typeof import('../../client/components/static-generation-searchparams-bailout-provider') -const { createSearchParamsBailoutProxy } = - require('next/dist/client/components/searchparams-bailout-proxy') as typeof import('../../client/components/searchparams-bailout-proxy') - -const serverHooks = - require('next/dist/client/components/hooks-server-context') as typeof import('../../client/components/hooks-server-context') - const { renderToReadableStream, decodeReply, decodeAction, // eslint-disable-next-line import/no-extraneous-dependencies } = require('react-server-dom-webpack/server.edge') -const { preloadStyle, preloadFont, preconnect } = - require('next/dist/server/app-render/rsc/preloads') as typeof import('../../server/app-render/rsc/preloads') + +import AppRouter from '../../client/components/app-router' +import LayoutRouter from '../../client/components/layout-router' +import RenderFromTemplateContext from '../../client/components/render-from-template-context' +import { staticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage.external' +import { requestAsyncStorage } from '../../client/components/request-async-storage.external' +import { actionAsyncStorage } from '../../client/components/action-async-storage.external' +import { staticGenerationBailout } from '../../client/components/static-generation-bailout' +import StaticGenerationSearchParamsBailoutProvider from '../../client/components/static-generation-searchparams-bailout-provider' +import { createSearchParamsBailoutProxy } from '../../client/components/searchparams-bailout-proxy' +import * as serverHooks from '../../client/components/hooks-server-context' + +import { + preloadStyle, + preloadFont, + preconnect, +} from '../../server/app-render/rsc/preloads' const { NotFoundBoundary } = require('next/dist/client/components/not-found-boundary') as typeof import('../../client/components/not-found-boundary') diff --git a/packages/next/src/server/app-render/server-inserted-html.tsx b/packages/next/src/server/app-render/server-inserted-html.tsx index f044c24feaba3..764dc62792077 100644 --- a/packages/next/src/server/app-render/server-inserted-html.tsx +++ b/packages/next/src/server/app-render/server-inserted-html.tsx @@ -2,7 +2,7 @@ // elements into the HTML stream. import React from 'react' -import { ServerInsertedHTMLContext } from '../../shared/lib/server-inserted-html' +import { ServerInsertedHTMLContext } from '../../shared/lib/server-inserted-html.shared-runtime' export function createServerInsertedHTML() { const serverInsertedHTMLCallbacks: (() => React.ReactNode)[] = [] diff --git a/packages/next/src/server/async-storage/request-async-storage-wrapper.ts b/packages/next/src/server/async-storage/request-async-storage-wrapper.ts index 50795855c53d4..1376ecfb197cd 100644 --- a/packages/next/src/server/async-storage/request-async-storage-wrapper.ts +++ b/packages/next/src/server/async-storage/request-async-storage-wrapper.ts @@ -1,7 +1,7 @@ import type { BaseNextRequest, BaseNextResponse } from '../base-http' import type { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'http' import type { AsyncLocalStorage } from 'async_hooks' -import type { RequestStore } from '../../client/components/request-async-storage' +import type { RequestStore } from '../../client/components/request-async-storage.external' import type { RenderOpts } from '../app-render/types' import type { AsyncStorageWrapper } from './async-storage-wrapper' import type { NextRequest } from '../web/spec-extension/request' diff --git a/packages/next/src/server/async-storage/static-generation-async-storage-wrapper.ts b/packages/next/src/server/async-storage/static-generation-async-storage-wrapper.ts index a28ac0e8ecb2a..d5adfc9de38b4 100644 --- a/packages/next/src/server/async-storage/static-generation-async-storage-wrapper.ts +++ b/packages/next/src/server/async-storage/static-generation-async-storage-wrapper.ts @@ -1,5 +1,5 @@ import type { AsyncStorageWrapper } from './async-storage-wrapper' -import type { StaticGenerationStore } from '../../client/components/static-generation-async-storage' +import type { StaticGenerationStore } from '../../client/components/static-generation-async-storage.external' import type { AsyncLocalStorage } from 'async_hooks' import type { IncrementalCache } from '../lib/incremental-cache' diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index d826f2bdd4e04..fab999db35ddc 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -55,7 +55,7 @@ import { getCookieParser, checkIsOnDemandRevalidate, } from './api-utils' -import { setConfig } from '../shared/lib/runtime-config' +import { setConfig } from '../shared/lib/runtime-config.shared-runtime' import { setRevalidateHeaders } from './send-payload/revalidate-headers' import { execOnce } from '../shared/lib/utils' @@ -427,7 +427,11 @@ export default abstract class Server { } = this.nextConfig this.buildId = this.getBuildId() - this.minimalMode = minimalMode || !!process.env.NEXT_PRIVATE_MINIMAL_MODE + // this is a hack to avoid Webpack knowing this is equal to this.minimalMode + // because we replace this.minimalMode to true in production bundles. + const minimalModeKey = 'minimalMode' + this[minimalModeKey] = + minimalMode || !!process.env.NEXT_PRIVATE_MINIMAL_MODE this.hasAppDir = this.getHasAppDir(dev) const serverComponents = this.hasAppDir diff --git a/packages/next/src/server/dev/next-dev-server.ts b/packages/next/src/server/dev/next-dev-server.ts index 1eca11b7358cb..839fa1b13c0e4 100644 --- a/packages/next/src/server/dev/next-dev-server.ts +++ b/packages/next/src/server/dev/next-dev-server.ts @@ -41,7 +41,7 @@ import { UnwrapPromise, withCoalescedInvoke, } from '../../lib/coalesced-function' -import { loadDefaultErrorComponents } from '../load-components' +import { loadDefaultErrorComponents } from '../load-default-error-components' import { DecodeError, MiddlewareNotFoundError } from '../../shared/lib/utils' import * as Log from '../../build/output/log' import isError, { getProperError } from '../../lib/is-error' diff --git a/packages/next/src/server/dev/static-paths-worker.ts b/packages/next/src/server/dev/static-paths-worker.ts index 68932df7a36b0..ddd6526e52f23 100644 --- a/packages/next/src/server/dev/static-paths-worker.ts +++ b/packages/next/src/server/dev/static-paths-worker.ts @@ -14,8 +14,10 @@ import { loadComponents } from '../load-components' import { setHttpClientAndAgentOptions } from '../setup-http-agent-env' import { IncrementalCache } from '../lib/incremental-cache' import * as serverHooks from '../../client/components/hooks-server-context' -import { staticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage' -import { AppRouteRouteModule } from '../future/route-modules/app-route/module' +import { staticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage.external' + +const { AppRouteRouteModule } = + require('../future/route-modules/app-route/module.compiled') as typeof import('../future/route-modules/app-route/module') type RuntimeConfig = any @@ -56,7 +58,7 @@ export async function loadStaticPaths({ fallback?: boolean | 'blocking' }> { // update work memory runtime-config - require('../../shared/lib/runtime-config').setConfig(config) + require('../../shared/lib/runtime-config.shared-runtime').setConfig(config) setHttpClientAndAgentOptions({ httpAgentOptions, }) diff --git a/packages/next/src/server/future/helpers/module-loader/node-module-loader.ts b/packages/next/src/server/future/helpers/module-loader/node-module-loader.ts index 3283eb00a53f5..6f70685df7e75 100644 --- a/packages/next/src/server/future/helpers/module-loader/node-module-loader.ts +++ b/packages/next/src/server/future/helpers/module-loader/node-module-loader.ts @@ -7,7 +7,10 @@ export class NodeModuleLoader implements ModuleLoader { public async load(id: string): Promise { if (process.env.NEXT_RUNTIME !== 'edge') { // Need to `await` to cover the case that route is marked ESM modules by ESM escalation. - return await require(id) + return await (process.env.NEXT_MINIMAL + ? // @ts-ignore + __non_webpack_require__(id) + : require(id)) } throw new Error('NodeModuleLoader is not supported in edge runtime.') diff --git a/packages/next/src/server/future/route-modules/app-page/module.compiled.ts b/packages/next/src/server/future/route-modules/app-page/module.compiled.ts new file mode 100644 index 0000000000000..78601739acbe5 --- /dev/null +++ b/packages/next/src/server/future/route-modules/app-page/module.compiled.ts @@ -0,0 +1,11 @@ +if (process.env.NEXT_RUNTIME === 'edge') { + module.exports = require('next/dist/server/future/route-modules/app-page/module.js') +} else { + if (process.env.NODE_ENV === 'development') { + module.exports = require('next/dist/compiled/next-server/app-page.runtime.dev.js') + } else if (process.env.TURBOPACK) { + module.exports = require('next/dist/compiled/next-server/app-page-turbo.runtime.prod.js') + } else { + module.exports = require('next/dist/compiled/next-server/app-page.runtime.prod.js') + } +} diff --git a/packages/next/src/server/future/route-modules/app-page/module.ts b/packages/next/src/server/future/route-modules/app-page/module.ts index 418e37420d7e9..daa0291a1c8b4 100644 --- a/packages/next/src/server/future/route-modules/app-page/module.ts +++ b/packages/next/src/server/future/route-modules/app-page/module.ts @@ -11,6 +11,7 @@ import { type RouteModuleOptions, type RouteModuleHandleContext, } from '../route-module' +import * as sharedModules from './shared-modules' type AppPageUserlandModule = { /** @@ -34,6 +35,8 @@ export class AppPageRouteModule extends RouteModule< AppPageRouteDefinition, AppPageUserlandModule > { + static readonly sharedModules = sharedModules + public render( req: IncomingMessage, res: ServerResponse, @@ -49,4 +52,6 @@ export class AppPageRouteModule extends RouteModule< } } +export { renderToHTMLOrFlight } + export default AppPageRouteModule diff --git a/packages/next/src/server/future/route-modules/app-page/shared-modules.ts b/packages/next/src/server/future/route-modules/app-page/shared-modules.ts new file mode 100644 index 0000000000000..e986c1bad3894 --- /dev/null +++ b/packages/next/src/server/future/route-modules/app-page/shared-modules.ts @@ -0,0 +1,13 @@ +// the name of the export has to be the camelCase version of the file name (without the extension) +export * as headManagerContext from '../../../../shared/lib/head-manager-context.shared-runtime' +export * as serverInsertedHtml from '../../../../shared/lib/server-inserted-html.shared-runtime' +export * as appRouterContext from '../../../../shared/lib/app-router-context.shared-runtime' +export * as hooksClientContext from '../../../../shared/lib/hooks-client-context.shared-runtime' +export * as routerContext from '../../../../shared/lib/router-context.shared-runtime' +export * as htmlContext from '../../../../shared/lib/html-context.shared-runtime' +export * as ampContext from '../../../../shared/lib/amp-context.shared-runtime' +export * as adapters from '../../../../shared/lib/router/adapters.shared-runtime' +export * as loadableContext from '../../../../shared/lib/loadable-context.shared-runtime' +export * as imageConfigContext from '../../../../shared/lib/image-config-context.shared-runtime' +export * as runtimeConfig from '../../../../shared/lib/runtime-config.shared-runtime' +export * as loadable from '../../../../shared/lib/loadable.shared-runtime' diff --git a/packages/next/src/server/future/route-modules/app-route/module.compiled.ts b/packages/next/src/server/future/route-modules/app-route/module.compiled.ts new file mode 100644 index 0000000000000..f5909104bc772 --- /dev/null +++ b/packages/next/src/server/future/route-modules/app-route/module.compiled.ts @@ -0,0 +1,11 @@ +if (process.env.NEXT_RUNTIME === 'edge') { + module.exports = require('next/dist/server/future/route-modules/app-route/module.js') +} else { + if (process.env.NODE_ENV === 'development') { + module.exports = require('next/dist/compiled/next-server/app-route.runtime.dev.js') + } else if (process.env.TURBOPACK) { + module.exports = require('next/dist/compiled/next-server/app-route-turbo.runtime.prod.js') + } else { + module.exports = require('next/dist/compiled/next-server/app-route.runtime.prod.js') + } +} diff --git a/packages/next/src/server/future/route-modules/app-route/module.ts b/packages/next/src/server/future/route-modules/app-route/module.ts index bf47738d35fea..d1d36d31501e4 100644 --- a/packages/next/src/server/future/route-modules/app-route/module.ts +++ b/packages/next/src/server/future/route-modules/app-route/module.ts @@ -35,22 +35,14 @@ import { appendMutableCookies } from '../../../web/spec-extension/adapters/reque import { RouteKind } from '../../route-kind' import { parsedUrlQueryToParams } from './helpers/parsed-url-query-to-params' -// These are imported weirdly like this because of the way that the bundling -// works. We need to import the built files from the dist directory, but we -// can't do that directly because we need types from the source files. So we -// import the types from the source files and then import the built files. -const { requestAsyncStorage } = - require('next/dist/client/components/request-async-storage') as typeof import('../../../../client/components/request-async-storage') -const { staticGenerationAsyncStorage } = - require('next/dist/client/components/static-generation-async-storage') as typeof import('../../../../client/components/static-generation-async-storage') -const serverHooks = - require('next/dist/client/components/hooks-server-context') as typeof import('../../../../client/components/hooks-server-context') -const headerHooks = - require('next/dist/client/components/headers') as typeof import('../../../../client/components/headers') -const { staticGenerationBailout } = - require('next/dist/client/components/static-generation-bailout') as typeof import('../../../../client/components/static-generation-bailout') -const { actionAsyncStorage } = - require('next/dist/client/components/action-async-storage') as typeof import('../../../../client/components/action-async-storage') +import * as serverHooks from '../../../../client/components/hooks-server-context' +import * as headerHooks from '../../../../client/components/headers' +import { staticGenerationBailout } from '../../../../client/components/static-generation-bailout' + +import { requestAsyncStorage } from '../../../../client/components/request-async-storage.external' +import { staticGenerationAsyncStorage } from '../../../../client/components/static-generation-async-storage.external' +import { actionAsyncStorage } from '../../../../client/components/action-async-storage.external' +import * as sharedModules from './shared-modules' /** * AppRouteRouteHandlerContext is the context that is passed to the route @@ -147,6 +139,8 @@ export class AppRouteRouteModule extends RouteModule< */ public readonly staticGenerationBailout = staticGenerationBailout + public static readonly sharedModules = sharedModules + /** * A reference to the mutation related async storage, such as mutations of * cookies. diff --git a/packages/next/src/server/future/route-modules/app-route/shared-modules.ts b/packages/next/src/server/future/route-modules/app-route/shared-modules.ts new file mode 100644 index 0000000000000..e6139d5a69404 --- /dev/null +++ b/packages/next/src/server/future/route-modules/app-route/shared-modules.ts @@ -0,0 +1,3 @@ +// the name of the export has to be the camelCase version of the file name (without the extension) +// TODO: remove this. We need it because using notFound from next/navigation imports this file :( +export * as appRouterContext from '../../../../shared/lib/app-router-context.shared-runtime' diff --git a/packages/next/src/server/future/route-modules/pages-api/module.compiled.ts b/packages/next/src/server/future/route-modules/pages-api/module.compiled.ts new file mode 100644 index 0000000000000..ed74c41adb918 --- /dev/null +++ b/packages/next/src/server/future/route-modules/pages-api/module.compiled.ts @@ -0,0 +1,11 @@ +if (process.env.NEXT_RUNTIME === 'edge') { + module.exports = require('next/dist/server/future/route-modules/pages-api/module.js') +} else { + if (process.env.NODE_ENV === 'development') { + module.exports = require('next/dist/compiled/next-server/pages-api.runtime.dev.js') + } else if (process.env.TURBOPACK) { + module.exports = require('next/dist/compiled/next-server/pages-api-turbo.runtime.prod.js') + } else { + module.exports = require('next/dist/compiled/next-server/pages-api.runtime.prod.js') + } +} diff --git a/packages/next/src/server/future/route-modules/pages-api/module.ts b/packages/next/src/server/future/route-modules/pages-api/module.ts index 88dbda73b464c..976daeeec4a87 100644 --- a/packages/next/src/server/future/route-modules/pages-api/module.ts +++ b/packages/next/src/server/future/route-modules/pages-api/module.ts @@ -100,6 +100,16 @@ export class PagesAPIRouteModule extends RouteModule< PagesAPIRouteDefinition, PagesAPIUserlandModule > { + constructor(options: PagesAPIRouteModuleOptions) { + super(options) + + if (typeof options.userland.default !== 'function') { + throw new Error( + `Page ${options.definition.page} does not export a default function.` + ) + } + } + /** * * @param req the incoming server request diff --git a/packages/next/src/server/future/route-modules/pages/module.compiled.ts b/packages/next/src/server/future/route-modules/pages/module.compiled.ts new file mode 100644 index 0000000000000..a935b62abdcad --- /dev/null +++ b/packages/next/src/server/future/route-modules/pages/module.compiled.ts @@ -0,0 +1,11 @@ +if (process.env.NEXT_RUNTIME === 'edge') { + module.exports = require('next/dist/server/future/route-modules/pages/module.js') +} else { + if (process.env.NODE_ENV === 'development') { + module.exports = require('next/dist/compiled/next-server/pages.runtime.dev.js') + } else if (process.env.TURBOPACK) { + module.exports = require('next/dist/compiled/next-server/pages-turbo.runtime.prod.js') + } else { + module.exports = require('next/dist/compiled/next-server/pages.runtime.prod.js') + } +} diff --git a/packages/next/src/server/future/route-modules/pages/module.ts b/packages/next/src/server/future/route-modules/pages/module.ts index dac8ae5546441..e2730ef668901 100644 --- a/packages/next/src/server/future/route-modules/pages/module.ts +++ b/packages/next/src/server/future/route-modules/pages/module.ts @@ -17,7 +17,8 @@ import { type RouteModuleHandleContext, type RouteModuleOptions, } from '../route-module' -import { renderToHTMLImpl } from '../../../render' +import { renderToHTMLImpl, renderToHTML } from '../../../render' +import * as sharedModules from './shared-modules' /** * The userland module for a page. This is the module that is exported from the @@ -104,6 +105,8 @@ export class PagesRouteModule extends RouteModule< > { private readonly components: PagesComponents + static readonly sharedModules = sharedModules + constructor(options: PagesRouteModuleOptions) { super(options) @@ -129,4 +132,7 @@ export class PagesRouteModule extends RouteModule< } } +// needed for the static build +export { renderToHTML } + export default PagesRouteModule diff --git a/packages/next/src/server/future/route-modules/pages/shared-modules.ts b/packages/next/src/server/future/route-modules/pages/shared-modules.ts new file mode 100644 index 0000000000000..55cdfbdeca37c --- /dev/null +++ b/packages/next/src/server/future/route-modules/pages/shared-modules.ts @@ -0,0 +1,12 @@ +// the name of the export has to be the camelCase version of the file name (without the extension) +export * as htmlContext from '../../../../shared/lib/html-context.shared-runtime' +export * as routerContext from '../../../../shared/lib/router-context.shared-runtime' +export * as ampContext from '../../../../shared/lib/amp-context.shared-runtime' +export * as headManagerContext from '../../../../shared/lib/head-manager-context.shared-runtime' +export * as adapters from '../../../../shared/lib/router/adapters.shared-runtime' +export * as loadableContext from '../../../../shared/lib/loadable-context.shared-runtime' +export * as appRouterContext from '../../../../shared/lib/app-router-context.shared-runtime' +export * as hooksClientContext from '../../../../shared/lib/hooks-client-context.shared-runtime' +export * as imageConfigContext from '../../../../shared/lib/image-config-context.shared-runtime' +export * as runtimeConfig from '../../../../shared/lib/runtime-config.shared-runtime' +export * as loadable from '../../../../shared/lib/loadable.shared-runtime' diff --git a/packages/next/src/server/future/route-modules/route-module.ts b/packages/next/src/server/future/route-modules/route-module.ts index 52188ed506dff..a8e5dd6c5945a 100644 --- a/packages/next/src/server/future/route-modules/route-module.ts +++ b/packages/next/src/server/future/route-modules/route-module.ts @@ -44,6 +44,11 @@ export abstract class RouteModule< */ public readonly definition: Readonly + /** + * The shared modules that are exposed and required for the route module. + */ + public static readonly sharedModules: any + constructor({ userland, definition }: RouteModuleOptions) { this.userland = userland this.definition = definition diff --git a/packages/next/src/server/lib/incremental-cache/index.ts b/packages/next/src/server/lib/incremental-cache/index.ts index ae2b11b4dc00f..3032aa685300b 100644 --- a/packages/next/src/server/lib/incremental-cache/index.ts +++ b/packages/next/src/server/lib/incremental-cache/index.ts @@ -131,7 +131,10 @@ export class IncrementalCache { maxMemoryCacheSize = parseInt(process.env.__NEXT_TEST_MAX_ISR_CACHE, 10) } this.dev = dev - this.minimalMode = minimalMode + // this is a hack to avoid Webpack knowing this is equal to this.minimalMode + // because we replace this.minimalMode to true in production bundles. + const minimalModeKey = 'minimalMode' + this[minimalModeKey] = minimalMode this.requestHeaders = requestHeaders this.requestProtocol = requestProtocol this.allowedRevalidateHeaderKeys = allowedRevalidateHeaderKeys diff --git a/packages/next/src/server/lib/patch-fetch.ts b/packages/next/src/server/lib/patch-fetch.ts index fb4d02c97b251..080361704b718 100644 --- a/packages/next/src/server/lib/patch-fetch.ts +++ b/packages/next/src/server/lib/patch-fetch.ts @@ -1,4 +1,4 @@ -import type { StaticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage' +import type { StaticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage.external' import type * as ServerHooks from '../../client/components/hooks-server-context' import { AppRenderSpan, NextNodeServerSpan } from './trace/constants' diff --git a/packages/next/src/server/lib/server-ipc/index.ts b/packages/next/src/server/lib/server-ipc/index.ts index 8e7bd60a8855d..023cbde2d25d8 100644 --- a/packages/next/src/server/lib/server-ipc/index.ts +++ b/packages/next/src/server/lib/server-ipc/index.ts @@ -115,6 +115,7 @@ export const createWorker = async ( __NEXT_PRIVATE_STANDALONE_CONFIG: process.env.__NEXT_PRIVATE_STANDALONE_CONFIG, NODE_ENV: process.env.NODE_ENV, + __NEXT_PRIVATE_RENDER_RUNTIME: type, __NEXT_PRIVATE_PREBUNDLED_REACT: type === 'app' ? (useServerActions ? 'experimental' : 'next') : '', ...(process.env.NEXT_CPU_PROF diff --git a/packages/next/src/server/lib/trace/constants.ts b/packages/next/src/server/lib/trace/constants.ts index 50eb4528c7dec..1b45358bdf8ed 100644 --- a/packages/next/src/server/lib/trace/constants.ts +++ b/packages/next/src/server/lib/trace/constants.ts @@ -43,7 +43,6 @@ enum NextNodeServerSpan { generatePublicRoutes = 'NextNodeServer.generatePublicRoutes', generateImageRoutes = 'NextNodeServer.generateImageRoutes.route', sendRenderResult = 'NextNodeServer.sendRenderResult', - sendStatic = 'NextNodeServer.sendStatic', proxyRequest = 'NextNodeServer.proxyRequest', runApi = 'NextNodeServer.runApi', render = 'NextNodeServer.render', diff --git a/packages/next/src/server/load-components.ts b/packages/next/src/server/load-components.ts index d2b563493ebe6..39da802ff39b1 100644 --- a/packages/next/src/server/load-components.ts +++ b/packages/next/src/server/load-components.ts @@ -55,7 +55,7 @@ export type LoadComponentsReturnType = { /** * Load manifest file with retries, defaults to 3 attempts. */ -async function loadManifestWithRetries( +export async function loadManifestWithRetries( manifestPath: string, attempts = 3 ): Promise { @@ -87,34 +87,6 @@ async function loadJSManifest( } } -async function loadDefaultErrorComponentsImpl( - distDir: string -): Promise { - const Document = interopDefault(require('next/dist/pages/_document')) - const AppMod = require('next/dist/pages/_app') - const App = interopDefault(AppMod) - - // Load the compiled route module for this builtin error. - // TODO: (wyattjoh) replace this with just exporting the route module when the transition is complete - const ComponentMod = - require('./future/route-modules/pages/builtin/_error') as typeof import('./future/route-modules/pages/builtin/_error') - const Component = ComponentMod.routeModule.userland.default - - return { - App, - Document, - Component, - pageConfig: {}, - buildManifest: await loadManifestWithRetries( - join(distDir, `fallback-${BUILD_MANIFEST}`) - ), - reactLoadableManifest: {}, - ComponentMod, - pathname: '/_error', - routeModule: ComponentMod.routeModule, - } -} - async function loadComponentsImpl({ distDir, pathname, @@ -205,8 +177,3 @@ export const loadComponents = getTracer().wrap( LoadComponentsSpan.loadComponents, loadComponentsImpl ) - -export const loadDefaultErrorComponents = getTracer().wrap( - LoadComponentsSpan.loadDefaultErrorComponents, - loadDefaultErrorComponentsImpl -) diff --git a/packages/next/src/server/load-default-error-components.ts b/packages/next/src/server/load-default-error-components.ts new file mode 100644 index 0000000000000..c390e9180b3d9 --- /dev/null +++ b/packages/next/src/server/load-default-error-components.ts @@ -0,0 +1,78 @@ +import type { + AppType, + DocumentType, + NextComponentType, +} from '../shared/lib/utils' +import type { ClientReferenceManifest } from '../build/webpack/plugins/flight-manifest-plugin' +import type { + PageConfig, + GetStaticPaths, + GetServerSideProps, + GetStaticProps, +} from 'next/types' +import type { RouteModule } from './future/route-modules/route-module' + +import { BUILD_MANIFEST } from '../shared/lib/constants' +import { join } from 'path' +import { BuildManifest } from './get-page-files' +import { interopDefault } from '../lib/interop-default' +import { getTracer } from './lib/trace/tracer' +import { LoadComponentsSpan } from './lib/trace/constants' +import { loadManifestWithRetries } from './load-components' +export type ManifestItem = { + id: number | string + files: string[] +} + +export type ReactLoadableManifest = { [moduleId: string]: ManifestItem } + +export type LoadComponentsReturnType = { + Component: NextComponentType + pageConfig: PageConfig + buildManifest: BuildManifest + subresourceIntegrityManifest?: Record + reactLoadableManifest: ReactLoadableManifest + clientReferenceManifest?: ClientReferenceManifest + serverActionsManifest?: any + Document: DocumentType + App: AppType + getStaticProps?: GetStaticProps + getStaticPaths?: GetStaticPaths + getServerSideProps?: GetServerSideProps + ComponentMod: any + routeModule?: RouteModule + isAppPath?: boolean + pathname: string +} + +async function loadDefaultErrorComponentsImpl( + distDir: string +): Promise { + const Document = interopDefault(require('next/dist/pages/_document')) + const AppMod = require('next/dist/pages/_app') + const App = interopDefault(AppMod) + + // Load the compiled route module for this builtin error. + // TODO: (wyattjoh) replace this with just exporting the route module when the transition is complete + const ComponentMod = + require('./future/route-modules/pages/builtin/_error') as typeof import('./future/route-modules/pages/builtin/_error') + const Component = ComponentMod.routeModule.userland.default + + return { + App, + Document, + Component, + pageConfig: {}, + buildManifest: await loadManifestWithRetries( + join(distDir, `fallback-${BUILD_MANIFEST}`) + ), + reactLoadableManifest: {}, + ComponentMod, + pathname: '/_error', + routeModule: ComponentMod.routeModule, + } +} +export const loadDefaultErrorComponents = getTracer().wrap( + LoadComponentsSpan.loadDefaultErrorComponents, + loadDefaultErrorComponentsImpl +) diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 087752225090f..7abf3605541ad 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -15,7 +15,7 @@ import { import type { MiddlewareManifest } from '../build/webpack/plugins/middleware-plugin' import type RenderResult from './render-result' import type { FetchEventResult } from './web/types' -import type { PrerenderManifest, RoutesManifest } from '../build' +import type { PrerenderManifest } from '../build' import { BaseNextRequest, BaseNextResponse } from './base-http' import type { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin' import type { PayloadOptions } from './send-payload' @@ -26,7 +26,6 @@ import { } from '../shared/lib/router/utils/route-matcher' import type { MiddlewareRouteMatch } from '../shared/lib/router/utils/middleware-route-matcher' import type { RouteMatch } from './future/route-matches/route-match' -import { renderToHTML, type RenderOpts } from './render' import fs from 'fs' import { join, resolve, isAbsolute } from 'path' @@ -49,7 +48,6 @@ import { findDir } from '../lib/find-pages-dir' import { UrlWithParsedQuery } from 'url' import { NodeNextRequest, NodeNextResponse } from './base-http/node' import { sendRenderResult } from './send-payload' -import { getExtension, serveStatic } from './serve-static' import { ParsedUrlQuery } from 'querystring' import { ParsedUrl, parseUrl } from '../shared/lib/router/utils/parse-url' import * as Log from '../build/output/log' @@ -96,7 +94,6 @@ import { invokeRequest } from './lib/server-ipc/invoke-request' import { pipeReadable } from './pipe-readable' import { filterReqHeaders, ipcForbiddenHeaders } from './lib/server-ipc/utils' import { createRequestResponseMocks } from './lib/mock-request' -import chalk from 'next/dist/compiled/chalk' import { NEXT_RSC_UNION_QUERY } from '../client/components/app-router-headers' import { signalFromNodeResponse } from './web/spec-extension/adapters/next-request' import { RouteModuleLoader } from './future/helpers/module-loader/route-module-loader' @@ -382,14 +379,6 @@ export default class NextNodeServer extends BaseServer { }) } - protected sendStatic( - req: NodeNextRequest, - res: NodeNextResponse, - path: string - ): Promise { - return serveStatic(req.originalRequest, res.originalResponse, path) - } - protected async runApi( req: BaseNextRequest | NodeNextRequest, res: BaseNextResponse | NodeNextResponse, @@ -452,7 +441,7 @@ export default class NextNodeServer extends BaseServer { res: NodeNextResponse, pathname: string, query: NextParsedUrlQuery, - renderOpts: RenderOpts + renderOpts: import('./render').RenderOpts ): Promise { return getTracer().trace(NextNodeServerSpan.renderHTML, async () => this.renderHTMLImpl(req, res, pathname, query, renderOpts) @@ -464,17 +453,35 @@ export default class NextNodeServer extends BaseServer { res: NodeNextResponse, pathname: string, query: NextParsedUrlQuery, - renderOpts: RenderOpts + renderOpts: import('./render').RenderOpts ): Promise { - // Due to the way we pass data by mutating `renderOpts`, we can't extend the - // object here but only updating its `nextFontManifest` field. - // https://github.com/vercel/next.js/blob/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952 - renderOpts.nextFontManifest = this.nextFontManifest - - if (this.hasAppDir && renderOpts.isAppPath) { - const { renderToHTMLOrFlight: appRenderToHTMLOrFlight } = - require('./app-render/app-render') as typeof import('./app-render/app-render') - return appRenderToHTMLOrFlight( + if (process.env.NEXT_MINIMAL) { + throw new Error( + 'invariant: renderHTML should not be called in minimal mode' + ) + // the `else` branch is needed for tree-shaking + } else { + // Due to the way we pass data by mutating `renderOpts`, we can't extend the + // object here but only updating its `nextFontManifest` field. + // https://github.com/vercel/next.js/blob/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952 + renderOpts.nextFontManifest = this.nextFontManifest + + if (this.hasAppDir && renderOpts.isAppPath) { + const { renderToHTMLOrFlight: appRenderToHTMLOrFlight } = + require('./future/route-modules/app-page/module.compiled') as typeof import('./app-render/app-render') + return appRenderToHTMLOrFlight( + req.originalRequest, + res.originalResponse, + pathname, + query, + renderOpts + ) + } + + // TODO: re-enable this once we've refactored to use implicit matches + // throw new Error('Invariant: render should have used routeModule') + + return require('./future/route-modules/pages/module.compiled').renderToHTML( req.originalRequest, res.originalResponse, pathname, @@ -482,17 +489,6 @@ export default class NextNodeServer extends BaseServer { renderOpts ) } - - // TODO: re-enable this once we've refactored to use implicit matches - // throw new Error('Invariant: render should have used routeModule') - - return renderToHTML( - req.originalRequest, - res.originalResponse, - pathname, - query, - renderOpts - ) } protected async imageOptimizer( @@ -500,55 +496,63 @@ export default class NextNodeServer extends BaseServer { res: NodeNextResponse, paramsResult: import('./image-optimizer').ImageParamsResult ): Promise<{ buffer: Buffer; contentType: string; maxAge: number }> { - const { imageOptimizer } = - require('./image-optimizer') as typeof import('./image-optimizer') - - return imageOptimizer( - req.originalRequest, - res.originalResponse, - paramsResult, - this.nextConfig, - this.renderOpts.dev, - async (newReq, newRes, newParsedUrl) => { - if (newReq.url === req.url) { - throw new Error(`Invariant attempted to optimize _next/image itself`) - } - - if (this.isRenderWorker) { - const invokeRes = await invokeRequest( - `http://${this.fetchHostname || 'localhost'}:${this.port}${ - newReq.url || '' - }`, - { - method: newReq.method || 'GET', - headers: newReq.headers, - signal: signalFromNodeResponse(res.originalResponse), - } - ) - const filteredResHeaders = filterReqHeaders( - toNodeOutgoingHttpHeaders(invokeRes.headers), - ipcForbiddenHeaders - ) + if (process.env.NEXT_MINIMAL) { + throw new Error( + 'invariant: imageOptimizer should not be called in minimal mode' + ) + } else { + const { imageOptimizer } = + require('./image-optimizer') as typeof import('./image-optimizer') - for (const key of Object.keys(filteredResHeaders)) { - newRes.setHeader(key, filteredResHeaders[key] || '') + return imageOptimizer( + req.originalRequest, + res.originalResponse, + paramsResult, + this.nextConfig, + this.renderOpts.dev, + async (newReq, newRes, newParsedUrl) => { + if (newReq.url === req.url) { + throw new Error( + `Invariant attempted to optimize _next/image itself` + ) } - newRes.statusCode = invokeRes.status || 200 - if (invokeRes.body) { - await pipeReadable(invokeRes.body, newRes) - } else { - res.send() + if (this.isRenderWorker) { + const invokeRes = await invokeRequest( + `http://${this.fetchHostname || 'localhost'}:${this.port}${ + newReq.url || '' + }`, + { + method: newReq.method || 'GET', + headers: newReq.headers, + signal: signalFromNodeResponse(res.originalResponse), + } + ) + const filteredResHeaders = filterReqHeaders( + toNodeOutgoingHttpHeaders(invokeRes.headers), + ipcForbiddenHeaders + ) + + for (const key of Object.keys(filteredResHeaders)) { + newRes.setHeader(key, filteredResHeaders[key] || '') + } + newRes.statusCode = invokeRes.status || 200 + + if (invokeRes.body) { + await pipeReadable(invokeRes.body, newRes) + } else { + res.send() + } + return } - return + return this.getRequestHandler()( + new NodeNextRequest(newReq), + new NodeNextResponse(newRes), + newParsedUrl + ) } - return this.getRequestHandler()( - new NodeNextRequest(newReq), - new NodeNextResponse(newRes), - newParsedUrl - ) - } - ) + ) + } } protected getPagePath(pathname: string, locales?: string[]): string { @@ -719,99 +723,109 @@ export default class NextNodeServer extends BaseServer { res: BaseNextResponse, parsedUrl: NextUrlWithParsedQuery ) { - if (this.minimalMode || this.nextConfig.output === 'export') { + if ( + this.minimalMode || + this.nextConfig.output === 'export' || + process.env.NEXT_MINIMAL + ) { res.statusCode = 400 res.body('Bad Request').send() return { finished: true, } - } - const { ImageOptimizerCache } = - require('./image-optimizer') as typeof import('./image-optimizer') + // the `else` branch is needed for tree-shaking + } else { + const { ImageOptimizerCache } = + require('./image-optimizer') as typeof import('./image-optimizer') - const imageOptimizerCache = new ImageOptimizerCache({ - distDir: this.distDir, - nextConfig: this.nextConfig, - }) + const imageOptimizerCache = new ImageOptimizerCache({ + distDir: this.distDir, + nextConfig: this.nextConfig, + }) - const { getHash, sendResponse, ImageError } = - require('./image-optimizer') as typeof import('./image-optimizer') + const { getHash, sendResponse, ImageError } = + require('./image-optimizer') as typeof import('./image-optimizer') - if (!this.imageResponseCache) { - throw new Error('invariant image optimizer cache was not initialized') - } - const imagesConfig = this.nextConfig.images + if (!this.imageResponseCache) { + throw new Error('invariant image optimizer cache was not initialized') + } + const imagesConfig = this.nextConfig.images - if (imagesConfig.loader !== 'default' || imagesConfig.unoptimized) { - await this.render404(req, res) - return { finished: true } - } - const paramsResult = ImageOptimizerCache.validateParams( - (req as NodeNextRequest).originalRequest, - parsedUrl.query, - this.nextConfig, - !!this.renderOpts.dev - ) + if (imagesConfig.loader !== 'default' || imagesConfig.unoptimized) { + await this.render404(req, res) + return { finished: true } + } + const paramsResult = ImageOptimizerCache.validateParams( + (req as NodeNextRequest).originalRequest, + parsedUrl.query, + this.nextConfig, + !!this.renderOpts.dev + ) - if ('errorMessage' in paramsResult) { - res.statusCode = 400 - res.body(paramsResult.errorMessage).send() - return { finished: true } - } - const cacheKey = ImageOptimizerCache.getCacheKey(paramsResult) + if ('errorMessage' in paramsResult) { + res.statusCode = 400 + res.body(paramsResult.errorMessage).send() + return { finished: true } + } + const cacheKey = ImageOptimizerCache.getCacheKey(paramsResult) - try { - const cacheEntry = await this.imageResponseCache.get( - cacheKey, - async () => { - const { buffer, contentType, maxAge } = await this.imageOptimizer( - req as NodeNextRequest, - res as NodeNextResponse, - paramsResult - ) - const etag = getHash([buffer]) + try { + const { getExtension } = + require('./serve-static') as typeof import('./serve-static') + const cacheEntry = await this.imageResponseCache.get( + cacheKey, + async () => { + const { buffer, contentType, maxAge } = await this.imageOptimizer( + req as NodeNextRequest, + res as NodeNextResponse, + paramsResult + ) + const etag = getHash([buffer]) + + return { + value: { + kind: 'IMAGE', + buffer, + etag, + extension: getExtension(contentType) as string, + }, + revalidate: maxAge, + } + }, + { + incrementalCache: imageOptimizerCache, + } + ) + if (cacheEntry?.value?.kind !== 'IMAGE') { + throw new Error( + 'invariant did not get entry from image response cache' + ) + } + sendResponse( + (req as NodeNextRequest).originalRequest, + (res as NodeNextResponse).originalResponse, + paramsResult.href, + cacheEntry.value.extension, + cacheEntry.value.buffer, + paramsResult.isStatic, + cacheEntry.isMiss ? 'MISS' : cacheEntry.isStale ? 'STALE' : 'HIT', + imagesConfig, + cacheEntry.revalidate || 0, + Boolean(this.renderOpts.dev) + ) + } catch (err) { + if (err instanceof ImageError) { + res.statusCode = err.statusCode + res.body(err.message).send() return { - value: { - kind: 'IMAGE', - buffer, - etag, - extension: getExtension(contentType) as string, - }, - revalidate: maxAge, + finished: true, } - }, - { - incrementalCache: imageOptimizerCache, - } - ) - - if (cacheEntry?.value?.kind !== 'IMAGE') { - throw new Error('invariant did not get entry from image response cache') - } - sendResponse( - (req as NodeNextRequest).originalRequest, - (res as NodeNextResponse).originalResponse, - paramsResult.href, - cacheEntry.value.extension, - cacheEntry.value.buffer, - paramsResult.isStatic, - cacheEntry.isMiss ? 'MISS' : cacheEntry.isStale ? 'STALE' : 'HIT', - imagesConfig, - cacheEntry.revalidate || 0, - Boolean(this.renderOpts.dev) - ) - } catch (err) { - if (err instanceof ImageError) { - res.statusCode = err.statusCode - res.body(err.message).send() - return { - finished: true, } + throw err } - throw err + return { finished: true } } - return { finished: true } } protected async handleCatchallRenderRequest( @@ -1012,6 +1026,7 @@ export default class NextNodeServer extends BaseServer { const enabledVerboseLogging = this.nextConfig.experimental.logging === 'verbose' if (this.renderOpts.dev) { + const chalk = require('next/dist/compiled/chalk') const _req = req as NodeNextRequest | IncomingMessage const _res = res as NodeNextResponse | ServerResponse const origReq = 'originalRequest' in _req ? _req.originalRequest : _req @@ -1432,6 +1447,12 @@ export default class NextNodeServer extends BaseServer { parsed: UrlWithParsedQuery onWarning?: (warning: Error) => void }) { + if (process.env.NEXT_MINIMAL) { + throw new Error( + 'invariant: runMiddleware should not be called in minimal mode' + ) + } + // Middleware is skipped for on-demand revalidate requests if ( checkIsOnDemandRevalidate(params.request, this.renderOpts.previewProps) @@ -1675,10 +1696,7 @@ export default class NextNodeServer extends BaseServer { protected getRoutesManifest(): NormalizedRouteManifest | undefined { return getTracer().trace(NextNodeServerSpan.getRoutesManifest, () => { - const manifest: RoutesManifest = require(join( - this.distDir, - ROUTES_MANIFEST - )) + const manifest = loadManifest(join(this.distDir, ROUTES_MANIFEST)) let rewrites = manifest.rewrites ?? { beforeFiles: [], @@ -1736,6 +1754,11 @@ export default class NextNodeServer extends BaseServer { match?: RouteMatch onWarning?: (warning: Error) => void }): Promise { + if (process.env.NEXT_MINIMAL) { + throw new Error( + 'Middleware is not supported in minimal mode. Please remove the `NEXT_MINIMAL` environment variable.' + ) + } let edgeInfo: ReturnType | undefined const { query, page, match } = params diff --git a/packages/next/src/server/render-result.ts b/packages/next/src/server/render-result.ts index b93dfce5fae21..2a8252e4fd143 100644 --- a/packages/next/src/server/render-result.ts +++ b/packages/next/src/server/render-result.ts @@ -1,4 +1,4 @@ -import { StaticGenerationStore } from '../client/components/static-generation-async-storage' +import { StaticGenerationStore } from '../client/components/static-generation-async-storage.external' import { pipeReadable, PipeTarget } from './pipe-readable' type ContentTypeOption = string | undefined diff --git a/packages/next/src/server/render.tsx b/packages/next/src/server/render.tsx index ac0989082aa9b..fe36e793348e2 100644 --- a/packages/next/src/server/render.tsx +++ b/packages/next/src/server/render.tsx @@ -1,7 +1,7 @@ import type { IncomingMessage, ServerResponse } from 'http' import type { ParsedUrlQuery } from 'querystring' import type { NextRouter } from '../shared/lib/router/router' -import type { HtmlProps } from '../shared/lib/html-context' +import type { HtmlProps } from '../shared/lib/html-context.shared-runtime' import type { DomainLocale } from './config' import type { AppType, @@ -52,12 +52,12 @@ import { } from '../shared/lib/constants' import { isSerializableProps } from '../lib/is-serializable-props' import { isInAmpMode } from '../shared/lib/amp-mode' -import { AmpStateContext } from '../shared/lib/amp-context' +import { AmpStateContext } from '../shared/lib/amp-context.shared-runtime' import { defaultHead } from '../shared/lib/head' -import { HeadManagerContext } from '../shared/lib/head-manager-context' -import Loadable from '../shared/lib/loadable' -import { LoadableContext } from '../shared/lib/loadable-context' -import { RouterContext } from '../shared/lib/router-context' +import { HeadManagerContext } from '../shared/lib/head-manager-context.shared-runtime' +import Loadable from '../shared/lib/loadable.shared-runtime' +import { LoadableContext } from '../shared/lib/loadable-context.shared-runtime' +import { RouterContext } from '../shared/lib/router-context.shared-runtime' import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic' import { ComponentsEnhancer, @@ -65,7 +65,7 @@ import { isResSent, loadGetInitialProps, } from '../shared/lib/utils' -import { HtmlContext } from '../shared/lib/html-context' +import { HtmlContext } from '../shared/lib/html-context.shared-runtime' import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path' import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path' import { getRequestMeta, NextParsedUrlQuery } from './request-meta' @@ -79,16 +79,16 @@ import { renderToInitialStream, continueFromInitialStream, } from './stream-utils/node-web-streams-helper' -import { ImageConfigContext } from '../shared/lib/image-config-context' +import { ImageConfigContext } from '../shared/lib/image-config-context.shared-runtime' import stripAnsi from 'next/dist/compiled/strip-ansi' import { stripInternalQueries } from './internal-utils' import { adaptForAppRouterInstance, adaptForSearchParams, PathnameContextProviderAdapter, -} from '../shared/lib/router/adapters' -import { AppRouterContext } from '../shared/lib/app-router-context' -import { SearchParamsContext } from '../shared/lib/hooks-client-context' +} from '../shared/lib/router/adapters.shared-runtime' +import { AppRouterContext } from '../shared/lib/app-router-context.shared-runtime' +import { SearchParamsContext } from '../shared/lib/hooks-client-context.shared-runtime' import { getTracer } from './lib/trace/tracer' import { RenderSpan } from './lib/trace/constants' import { ReflectAdapter } from './web/spec-extension/adapters/reflect' diff --git a/packages/next/src/server/require-hook.ts b/packages/next/src/server/require-hook.ts index 1f53b3b479109..9b2e1526eec0e 100644 --- a/packages/next/src/server/require-hook.ts +++ b/packages/next/src/server/require-hook.ts @@ -2,11 +2,13 @@ // This is needed for userland plugins to attach to the same webpack instance as Next.js'. // Individually compiled modules are as defined for the compilation in bundles/webpack/packages/*. +import path, { dirname } from 'path' + // This module will only be loaded once per process. -const { dirname } = require('path') const mod = require('module') const resolveFilename = mod._resolveFilename +const originalRequire = mod.prototype.require const hookPropertyMap = new Map() let aliasedPrebundledReact = false @@ -19,10 +21,9 @@ const resolve = process.env.NEXT_MINIMAL const toResolveMap = (map: Record): [string, string][] => Object.entries(map).map(([key, value]) => [key, resolve(value)]) -// these must use require.resolve to be statically analyzable export const defaultOverrides = { - 'styled-jsx': dirname(require.resolve('styled-jsx/package.json')), - 'styled-jsx/style': require.resolve('styled-jsx/style'), + 'styled-jsx': dirname(resolve('styled-jsx/package.json')), + 'styled-jsx/style': resolve('styled-jsx/style'), } export const baseOverrides = { @@ -78,7 +79,6 @@ export function addHookAliases(aliases: [string, string][] = []) { } } -// Add default aliases addHookAliases(toResolveMap(defaultOverrides)) // Override built-in React packages if necessary @@ -117,3 +117,29 @@ mod._resolveFilename = function ( // We use `bind` here to avoid referencing outside variables to create potential memory leaks. }.bind(null, resolveFilename, hookPropertyMap) + +// This is a hack to make sure that if a user requires a Next.js module that wasn't bundled +// that needs to point to the rendering runtime version, it will point to the correct one. +// This can happen on `pages` when a user requires a dependency that uses next/image for example. +// This is only needed in production as in development we fallback to the external version. +if ( + process.env.NODE_ENV !== 'development' && + process.env.__NEXT_PRIVATE_RENDER_RUNTIME && + !process.env.TURBOPACK +) { + const currentRuntime = `${ + process.env.__NEXT_PRIVATE_RENDER_RUNTIME === 'pages' + ? 'next/dist/compiled/next-server/pages.runtime' + : 'next/dist/compiled/next-server/app-page.runtime' + }.prod` + + mod.prototype.require = function (request: string) { + if (request.endsWith('.shared-runtime')) { + const base = path.basename(request, '.shared-runtime') + const camelized = base.replace(/-([a-z])/g, (g) => g[1].toUpperCase()) + const instance = originalRequire.call(this, currentRuntime) + return instance.default.sharedModules[camelized] + } + return originalRequire.call(this, request) + } +} diff --git a/packages/next/src/server/response-cache/index.ts b/packages/next/src/server/response-cache/index.ts index 6d135da26e939..7bd6710cc4a80 100644 --- a/packages/next/src/server/response-cache/index.ts +++ b/packages/next/src/server/response-cache/index.ts @@ -20,7 +20,10 @@ export default class ResponseCache { constructor(minimalMode: boolean) { this.pendingResponses = new Map() - this.minimalMode = minimalMode + // this is a hack to avoid Webpack knowing this is equal to this.minimalMode + // because we replace this.minimalMode to true in production bundles. + const minimalModeKey = 'minimalMode' + this[minimalModeKey] = minimalMode } public get( diff --git a/packages/next/src/server/response-cache/web.ts b/packages/next/src/server/response-cache/web.ts index e37ccca314812..f255fdd5412d4 100644 --- a/packages/next/src/server/response-cache/web.ts +++ b/packages/next/src/server/response-cache/web.ts @@ -15,7 +15,9 @@ export default class WebResponseCache { constructor(minimalMode: boolean) { this.pendingResponses = new Map() - this.minimalMode = minimalMode + // this is a hack to avoid Webpack knowing this is equal to this.minimalMode + // because we replace this.minimalMode to true in production bundles. + Object.assign(this, { minimalMode }) } public get( diff --git a/packages/next/src/server/web/adapter.ts b/packages/next/src/server/web/adapter.ts index 1ea32a75956cd..5c6ad3e7ed5ab 100644 --- a/packages/next/src/server/web/adapter.ts +++ b/packages/next/src/server/web/adapter.ts @@ -18,7 +18,7 @@ import { import { NEXT_QUERY_PARAM_PREFIX } from '../../lib/constants' import { ensureInstrumentationRegistered } from './globals' import { RequestAsyncStorageWrapper } from '../async-storage/request-async-storage-wrapper' -import { requestAsyncStorage } from '../../client/components/request-async-storage' +import { requestAsyncStorage } from '../../client/components/request-async-storage.external' import { PrerenderManifest } from '../../build' class NextRequestHint extends NextRequest { diff --git a/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts b/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts index df3c369eef877..d44ea986cad65 100644 --- a/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts +++ b/packages/next/src/server/web/spec-extension/adapters/request-cookies.ts @@ -1,5 +1,5 @@ import type { RequestCookies } from '../cookies' -import { StaticGenerationStore } from '../../../../client/components/static-generation-async-storage' +import { StaticGenerationStore } from '../../../../client/components/static-generation-async-storage.external' import { ResponseCookies } from '../cookies' import { ReflectAdapter } from './reflect' diff --git a/packages/next/src/server/web/spec-extension/revalidate-tag.ts b/packages/next/src/server/web/spec-extension/revalidate-tag.ts index 8d7cd68bd3a9a..7c7bff8c2f784 100644 --- a/packages/next/src/server/web/spec-extension/revalidate-tag.ts +++ b/packages/next/src/server/web/spec-extension/revalidate-tag.ts @@ -1,7 +1,7 @@ import { StaticGenerationAsyncStorage, StaticGenerationStore, -} from '../../../client/components/static-generation-async-storage' +} from '../../../client/components/static-generation-async-storage.external' export function revalidateTag(tag: string) { const staticGenerationAsyncStorage = ( diff --git a/packages/next/src/server/web/spec-extension/unstable-cache.ts b/packages/next/src/server/web/spec-extension/unstable-cache.ts index aad3ed2baf20a..1e85b5c290971 100644 --- a/packages/next/src/server/web/spec-extension/unstable-cache.ts +++ b/packages/next/src/server/web/spec-extension/unstable-cache.ts @@ -2,7 +2,7 @@ import { StaticGenerationStore, staticGenerationAsyncStorage as _staticGenerationAsyncStorage, StaticGenerationAsyncStorage, -} from '../../../client/components/static-generation-async-storage' +} from '../../../client/components/static-generation-async-storage.external' import { CACHE_ONE_YEAR } from '../../../lib/constants' import { addImplicitTags } from '../../lib/patch-fetch' diff --git a/packages/next/src/shared/lib/amp-context.ts b/packages/next/src/shared/lib/amp-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/amp-context.ts rename to packages/next/src/shared/lib/amp-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/amp.ts b/packages/next/src/shared/lib/amp.ts index 04518b2389357..8edd21db9c299 100644 --- a/packages/next/src/shared/lib/amp.ts +++ b/packages/next/src/shared/lib/amp.ts @@ -1,5 +1,5 @@ import React from 'react' -import { AmpStateContext } from './amp-context' +import { AmpStateContext } from './amp-context.shared-runtime' import { isInAmpMode } from './amp-mode' export function useAmp(): boolean { diff --git a/packages/next/src/shared/lib/app-router-context.ts b/packages/next/src/shared/lib/app-router-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/app-router-context.ts rename to packages/next/src/shared/lib/app-router-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/dynamic.tsx b/packages/next/src/shared/lib/dynamic.tsx index cb497fc587d4f..390410edda29e 100644 --- a/packages/next/src/shared/lib/dynamic.tsx +++ b/packages/next/src/shared/lib/dynamic.tsx @@ -1,5 +1,5 @@ import React from 'react' -import Loadable from './loadable' +import Loadable from './loadable.shared-runtime' const isServerSide = typeof window === 'undefined' diff --git a/packages/next/src/shared/lib/head-manager-context.ts b/packages/next/src/shared/lib/head-manager-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/head-manager-context.ts rename to packages/next/src/shared/lib/head-manager-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/head.tsx b/packages/next/src/shared/lib/head.tsx index 3156e259f656f..42f95767bfa4c 100644 --- a/packages/next/src/shared/lib/head.tsx +++ b/packages/next/src/shared/lib/head.tsx @@ -2,8 +2,8 @@ import React, { useContext } from 'react' import Effect from './side-effect' -import { AmpStateContext } from './amp-context' -import { HeadManagerContext } from './head-manager-context' +import { AmpStateContext } from './amp-context.shared-runtime' +import { HeadManagerContext } from './head-manager-context.shared-runtime' import { isInAmpMode } from './amp-mode' import { warnOnce } from './utils/warn-once' diff --git a/packages/next/src/shared/lib/hooks-client-context.ts b/packages/next/src/shared/lib/hooks-client-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/hooks-client-context.ts rename to packages/next/src/shared/lib/hooks-client-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/html-context.ts b/packages/next/src/shared/lib/html-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/html-context.ts rename to packages/next/src/shared/lib/html-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/image-config-context.ts b/packages/next/src/shared/lib/image-config-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/image-config-context.ts rename to packages/next/src/shared/lib/image-config-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/loadable-context.ts b/packages/next/src/shared/lib/loadable-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/loadable-context.ts rename to packages/next/src/shared/lib/loadable-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/loadable.tsx b/packages/next/src/shared/lib/loadable.shared-runtime.tsx similarity index 99% rename from packages/next/src/shared/lib/loadable.tsx rename to packages/next/src/shared/lib/loadable.shared-runtime.tsx index 1592d98551093..82ba84182701f 100644 --- a/packages/next/src/shared/lib/loadable.tsx +++ b/packages/next/src/shared/lib/loadable.shared-runtime.tsx @@ -23,7 +23,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE // Modified to be compatible with webpack 4 / Next.js import React from 'react' -import { LoadableContext } from './loadable-context' +import { LoadableContext } from './loadable-context.shared-runtime' function resolve(obj: any) { return obj && obj.default ? obj.default : obj diff --git a/packages/next/src/shared/lib/router-context.ts b/packages/next/src/shared/lib/router-context.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/router-context.ts rename to packages/next/src/shared/lib/router-context.shared-runtime.ts diff --git a/packages/next/src/shared/lib/router/adapters.tsx b/packages/next/src/shared/lib/router/adapters.shared-runtime.tsx similarity index 95% rename from packages/next/src/shared/lib/router/adapters.tsx rename to packages/next/src/shared/lib/router/adapters.shared-runtime.tsx index ce68a8bec1e8b..29f92dda8dc08 100644 --- a/packages/next/src/shared/lib/router/adapters.tsx +++ b/packages/next/src/shared/lib/router/adapters.shared-runtime.tsx @@ -1,7 +1,10 @@ import type { ParsedUrlQuery } from 'node:querystring' import React, { useMemo, useRef } from 'react' -import type { AppRouterInstance, NavigateOptions } from '../app-router-context' -import { PathnameContext } from '../hooks-client-context' +import type { + AppRouterInstance, + NavigateOptions, +} from '../app-router-context.shared-runtime' +import { PathnameContext } from '../hooks-client-context.shared-runtime' import type { NextRouter } from './router' import { isDynamicRoute } from './utils' diff --git a/packages/next/src/shared/lib/router/adapters.test.tsx b/packages/next/src/shared/lib/router/adapters.test.tsx index fa8e48f2fc088..e47ce2174dd35 100644 --- a/packages/next/src/shared/lib/router/adapters.test.tsx +++ b/packages/next/src/shared/lib/router/adapters.test.tsx @@ -1,4 +1,4 @@ -import { adaptForAppRouterInstance } from './adapters' +import { adaptForAppRouterInstance } from './adapters.shared-runtime' import { NextRouter } from './router' describe('adaptForAppRouterInstance', () => { diff --git a/packages/next/src/shared/lib/runtime-config.ts b/packages/next/src/shared/lib/runtime-config.shared-runtime.ts similarity index 100% rename from packages/next/src/shared/lib/runtime-config.ts rename to packages/next/src/shared/lib/runtime-config.shared-runtime.ts diff --git a/packages/next/src/shared/lib/server-inserted-html.tsx b/packages/next/src/shared/lib/server-inserted-html.shared-runtime.tsx similarity index 100% rename from packages/next/src/shared/lib/server-inserted-html.tsx rename to packages/next/src/shared/lib/server-inserted-html.shared-runtime.tsx diff --git a/packages/next/src/shared/lib/utils.ts b/packages/next/src/shared/lib/utils.ts index f967be459022b..4e8036a6894a2 100644 --- a/packages/next/src/shared/lib/utils.ts +++ b/packages/next/src/shared/lib/utils.ts @@ -1,4 +1,4 @@ -import type { HtmlProps } from './html-context' +import type { HtmlProps } from './html-context.shared-runtime' import type { ComponentType } from 'react' import type { DomainLocale } from '../../server/config' import type { Env } from '@next/env' diff --git a/packages/next/src/trace/index.ts b/packages/next/src/trace/index.ts index e242e19c9041f..e3928f775f613 100644 --- a/packages/next/src/trace/index.ts +++ b/packages/next/src/trace/index.ts @@ -1,4 +1,5 @@ import { trace, flushAllTraces, Span, SpanStatus } from './trace' import { SpanId, setGlobal } from './shared' -export { trace, flushAllTraces, SpanId, Span, SpanStatus, setGlobal } +export { trace, flushAllTraces, Span, setGlobal, SpanStatus } +export type { SpanId } diff --git a/packages/next/taskfile-webpack.js b/packages/next/taskfile-webpack.js new file mode 100644 index 0000000000000..04495d7b3621c --- /dev/null +++ b/packages/next/taskfile-webpack.js @@ -0,0 +1,35 @@ +const webpack = require('webpack') + +module.exports = function (task) { + task.plugin('webpack', {}, function* (_, options) { + options = options || {} + + const compiler = webpack(options.config) + + if (options.watch) { + compiler.watch({}, (err, stats) => { + if (err || stats.hasErrors()) { + console.error(err || stats.toString()) + } else { + console.log(`${options.name} compiled successfully.`) + } + }) + } else { + yield new Promise((resolve, reject) => { + compiler.run((err, stats) => { + if (err || stats.hasErrors()) { + console.error(err || stats.toString()) + reject(err || stats.toString()) + } + if (process.env.ANALYZE) { + require('fs').writeFileSync( + require('path').join(__dirname, options.name + '-stats.json'), + JSON.stringify(stats.toJson()) + ) + } + resolve() + }) + }) + } + }) +} diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index f38289c3d4984..321490234153b 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -2357,7 +2357,7 @@ export async function ncc(task, opts) { ) } -export async function compile(task, opts) { +export async function next_compile(task, opts) { await task.parallel( [ 'cli', @@ -2388,12 +2388,16 @@ export async function compile(task, opts) { ], opts ) +} + +export async function compile(task, opts) { + await task.serial(['next_compile', 'next_bundle'], opts) + await task.serial([ 'ncc_react_refresh_utils', 'ncc_next__react_dev_overlay', 'ncc_next_font', 'capsize_metrics', - 'minimal_next_server', ]) } @@ -2658,157 +2662,38 @@ export async function release(task) { await task.clear('dist').start('build') } -export async function minimal_next_server(task) { - const outputName = 'next-server.js' - const cachedOutputName = `${outputName}.cache` - - const minimalExternals = [ - 'react', - 'react/package.json', - 'react/jsx-runtime', - 'react/jsx-dev-runtime', - 'react-dom', - 'react-dom/package.json', - 'react-dom/client', - 'react-dom/server', - 'react-dom/server.browser', - 'react-dom/server.edge', - 'react-server-dom-webpack/client', - 'react-server-dom-webpack/client.edge', - 'react-server-dom-webpack/server.edge', - 'react-server-dom-webpack/server.node', - 'styled-jsx', - 'styled-jsx/style', - '@opentelemetry/api', - 'next/dist/compiled/@next/react-dev-overlay/dist/middleware', - 'next/dist/compiled/@ampproject/toolbox-optimizer', - 'next/dist/compiled/edge-runtime', - 'next/dist/compiled/@edge-runtime/ponyfill', - 'next/dist/compiled/undici', - 'next/dist/compiled/raw-body', - 'next/dist/server/capsize-font-metrics.json', - 'critters', - 'next/dist/compiled/node-html-parser', - 'next/dist/compiled/compression', - 'next/dist/compiled/jsonwebtoken', - 'next/dist/compiled/@mswjs/interceptors/ClientRequest', - ].reduce((acc, pkg) => { - acc[pkg] = pkg - return acc - }, {}) - - Object.assign(minimalExternals, { - '/(.*)config$/': 'next/dist/server/config', - './web/sandbox': 'next/dist/server/web/sandbox', +export async function next_bundle_prod(task, opts) { + await task.source('dist').webpack({ + watch: opts.dev, + config: require('./webpack.config')({ + dev: false, + }), + name: 'next-bundle-prod', }) +} - const webpack = require('webpack') - const TerserPlugin = require('terser-webpack-plugin') - // const BundleAnalyzerPlugin = - // require('webpack-bundle-analyzer').BundleAnalyzerPlugin - /** @type {webpack.Configuration} */ - const config = { - entry: join(__dirname, 'dist/server/next-server.js'), - target: 'node', - mode: 'production', - output: { - path: join(__dirname, 'dist/compiled/minimal-next-server'), - filename: outputName, - libraryTarget: 'commonjs2', - }, - // left in for debugging - optimization: { - moduleIds: 'named', - // minimize: false, - minimize: true, - minimizer: [ - new TerserPlugin({ - extractComments: false, - terserOptions: { - format: { - comments: false, - }, - compress: { - passes: 2, - }, - }, - }), - ], - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify('production'), - 'process.env.NEXT_MINIMAL': JSON.stringify('true'), - 'process.env.NEXT_RUNTIME': JSON.stringify('nodejs'), - }), - // new BundleAnalyzerPlugin({}), - ], - externals: [minimalExternals], - } - - await new Promise((resolve, reject) => { - webpack(config, (err, stats) => { - if (err) return reject(err) - if (stats.hasErrors()) { - return reject(new Error(stats.toString('errors-only'))) - } - resolve() - }) +export async function next_bundle_dev(task, opts) { + await task.source('dist').webpack({ + watch: opts.dev, + config: require('./webpack.config')({ + dev: true, + }), + name: 'next-bundle-dev', }) +} - const wrappedTemplate = ` -const filename = ${JSON.stringify(outputName)} -const { readFileSync } = require('fs'), - { Script } = require('vm'), - { wrap } = require('module'), - { join } = require('path'); -const basename = join(__dirname, filename) - -const source = readFileSync(basename, 'utf-8') - -const cachedData = - !process.pkg && - require('process').platform !== 'win32' && - readFileSync(join(__dirname, '${cachedOutputName}')) - -const scriptOpts = { filename: basename, columnOffset: 0 } - -const script = new Script( - wrap(source), - cachedData ? Object.assign({ cachedData }, scriptOpts) : scriptOpts -) - -script.runInThisContext()(exports, require, module, __filename, __dirname) -` - - await fs.writeFile( - join(__dirname, `dist/compiled/minimal-next-server/next-server-cached.js`), - wrappedTemplate - ) - - const Module = require('module') - const vm = require('vm') - const filename = resolve( - __dirname, - 'dist/compiled/minimal-next-server', - outputName - ) - - const content = require('fs').readFileSync(filename, 'utf8') - - const wrapper = Module.wrap(content) - var script = new vm.Script(wrapper, { - filename: filename, - lineOffset: 0, - displayErrors: true, +export async function next_bundle_turbo_prod(task, opts) { + await task.source('dist').webpack({ + watch: opts.dev, + config: require('./webpack.config')({ + turbo: true, + }), + name: 'next-bundle-prod-turbo', }) - - script.runInThisContext()(exports, require, module, __filename, __dirname) - - const buffer = script.createCachedData() - - await fs.writeFile( - join(__dirname, `dist/compiled/minimal-next-server/${cachedOutputName}`), - buffer +} +export async function next_bundle(task, opts) { + await task.parallel( + ['next_bundle_prod', 'next_bundle_dev', 'next_bundle_turbo_prod'], + opts ) } diff --git a/packages/next/webpack.config.js b/packages/next/webpack.config.js new file mode 100644 index 0000000000000..8e7c3063fa5ed --- /dev/null +++ b/packages/next/webpack.config.js @@ -0,0 +1,145 @@ +const webpack = require('webpack') +const path = require('path') +const TerserPlugin = require('terser-webpack-plugin') +const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') + +const minimalExternals = [ + 'react', + 'react/package.json', + 'react/jsx-runtime', + 'react/jsx-dev-runtime', + 'react-dom', + 'react-dom/package.json', + 'react-dom/client', + 'react-dom/server', + 'react-dom/server.browser', + 'react-dom/server.edge', + 'react-server-dom-webpack/client', + 'react-server-dom-webpack/client.edge', + 'react-server-dom-webpack/server.edge', + 'react-server-dom-webpack/server.node', + 'styled-jsx', + 'styled-jsx/style', + '@opentelemetry/api', + 'next/dist/compiled/@next/react-dev-overlay/dist/middleware', + 'next/dist/compiled/@ampproject/toolbox-optimizer', + 'next/dist/compiled/edge-runtime', + 'next/dist/compiled/@edge-runtime/ponyfill', + 'next/dist/compiled/undici', + 'next/dist/compiled/raw-body', + 'next/dist/server/capsize-font-metrics.json', + 'critters', + 'next/dist/compiled/node-html-parser', + 'next/dist/compiled/compression', + 'next/dist/compiled/jsonwebtoken', + 'next/dist/compiled/@opentelemetry/api', + 'next/dist/compiled/@mswjs/interceptors/ClientRequest', +] + +const externalsMap = { + './web/sandbox': 'next/dist/server/web/sandbox', +} + +const externalsRegexMap = { + '(.*)trace/tracer$': 'next/dist/server/lib/trace/tracer', +} + +module.exports = ({ dev, turbo }) => { + const externalHandler = ({ context, request, getResolve }, callback) => { + ;(async () => { + if ( + ((dev || turbo) && request.endsWith('.shared-runtime')) || + request.endsWith('.external') + ) { + const resolve = getResolve() + const resolved = await resolve(context, request) + const relative = path.relative( + path.join(__dirname, '..'), + resolved.replace('esm' + path.sep, '') + ) + callback(null, `commonjs ${relative}`) + } else { + const regexMatch = Object.keys(externalsRegexMap).find((regex) => + new RegExp(regex).test(request) + ) + if (regexMatch) { + return callback(null, 'commonjs ' + externalsRegexMap[regexMatch]) + } + callback() + } + })() + } + + /** @type {webpack.Configuration} */ + return { + entry: { + server: path.join(__dirname, 'dist/esm/server/next-server.js'), + 'app-page': path.join( + __dirname, + 'dist/esm/server/future/route-modules/app-page/module.js' + ), + 'app-route': path.join( + __dirname, + 'dist/esm/server/future/route-modules/app-route/module.js' + ), + pages: path.join( + __dirname, + 'dist/esm/server/future/route-modules/pages/module.js' + ), + 'pages-api': path.join( + __dirname, + 'dist/esm/server/future/route-modules/pages-api/module.js' + ), + }, + target: 'node', + mode: 'production', + output: { + path: path.join(__dirname, 'dist/compiled/next-server'), + filename: `[name]${turbo ? '-turbo' : ''}.runtime.${ + dev ? 'dev' : 'prod' + }.js`, + libraryTarget: 'commonjs2', + }, + optimization: { + moduleIds: 'named', + minimize: true, + // splitChunks: { + // chunks: 'all', + // }, + concatenateModules: true, + minimizer: [ + new TerserPlugin({ + extractComments: false, + terserOptions: { + format: { + comments: false, + }, + compress: { + passes: 2, + }, + }, + }), + ], + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env.NEXT_MINIMAL': JSON.stringify('true'), + 'this.serverOptions.experimentalTestProxy': JSON.stringify(false), + 'this.minimalMode': JSON.stringify(true), + 'this.renderOpts.dev': JSON.stringify(dev), + 'process.env.NODE_ENV': JSON.stringify( + dev ? 'development' : 'production' + ), + 'process.env.NEXT_RUNTIME': JSON.stringify('nodejs'), + }), + !!process.env.ANALYZE && + new BundleAnalyzerPlugin({ + analyzerPort: 8888 + (dev ? 0 : 1) + (turbo ? 1 : 0), + }), + ].filter(Boolean), + stats: { + optimizationBailout: true, + }, + externals: [...minimalExternals, externalsMap, externalHandler], + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ce02614924c0..c0bf75436a03f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1381,6 +1381,9 @@ importers: terser: specifier: 5.14.1 version: 5.14.1 + terser-webpack-plugin: + specifier: 5.3.9 + version: 5.3.9(@swc/core@1.3.55)(webpack@5.86.0) text-table: specifier: 0.2.0 version: 0.2.0 diff --git a/scripts/minimal-server.js b/scripts/minimal-server.js index f4f30ac97ce25..08f9125a4f0cc 100644 --- a/scripts/minimal-server.js +++ b/scripts/minimal-server.js @@ -1,3 +1,4 @@ +console.time('next-wall-time') // Usage: node scripts/minimal-server.js // This script is used to run a minimal Next.js server in production mode. @@ -44,11 +45,13 @@ if (process.env.LOG_READFILE) { require('fs').readFile = function (path, options, callback) { readFileCount++ + console.log(`readFile: ${path}`) return originalReadFile.apply(this, arguments) } require('fs').readFileSync = function (path, options) { readFileSyncCount++ + console.log(`readFileSync: ${path}`) return originalReadFileSync.apply(this, arguments) } } @@ -56,10 +59,9 @@ if (process.env.LOG_READFILE) { console.time('next-cold-start') const NextServer = process.env.USE_BUNDLED_NEXT - ? require('next/dist/compiled/minimal-next-server/next-server-cached').default + ? require('next/dist/compiled/next-server/server.runtime.prod').default : require('next/dist/server/next-server').default -console.timeEnd('next-cold-start') if (process.env.LOG_READFILE) { console.log(`readFileCount: ${readFileCount + readFileSyncCount}`) } @@ -101,9 +103,20 @@ require('http') if (process.env.LOG_READFILE) { console.log(`readFileCount: ${readFileCount + readFileSyncCount}`) } - require('process').exit(0) }) }) .listen(3000, () => { console.timeEnd('next-cold-start') + fetch('http://localhost:3000/') + .then((res) => res.text()) + .then((text) => { + console.log(text) + }) + .catch((err) => { + console.error(err) + }) + .finally(() => { + console.timeEnd('next-wall-time') + require('process').exit(0) + }) }) diff --git a/test/e2e/app-dir/actions/app-action-size-limit-invalid.test.ts b/test/e2e/app-dir/actions/app-action-size-limit-invalid.test.ts index a61e222c3872e..3e5e6b82ea050 100644 --- a/test/e2e/app-dir/actions/app-action-size-limit-invalid.test.ts +++ b/test/e2e/app-dir/actions/app-action-size-limit-invalid.test.ts @@ -113,7 +113,7 @@ createNextDescribe( await check(() => { const fullLog = logs.join('') - return fullLog.includes('Error: Body exceeded 1.5mb limit') && + return fullLog.includes('[Error]: Body exceeded 1.5mb limit') && fullLog.includes( 'To configure the body size limit for Server Actions, see' ) diff --git a/test/e2e/getserversideprops/app/pages/index.js b/test/e2e/getserversideprops/app/pages/index.js index 4433c9c2ee84e..da17edc01839d 100644 --- a/test/e2e/getserversideprops/app/pages/index.js +++ b/test/e2e/getserversideprops/app/pages/index.js @@ -1,6 +1,6 @@ import Link from 'next/link' import ReactDOM from 'react-dom/server' -import { RouterContext } from 'next/dist/shared/lib/router-context' +import { RouterContext } from 'next/dist/shared/lib/router-context.shared-runtime' import { useRouter } from 'next/router' function RouterComp(props) { diff --git a/test/e2e/opentelemetry/opentelemetry.test.ts b/test/e2e/opentelemetry/opentelemetry.test.ts index ae798ec7c5c2d..13e0fefea2787 100644 --- a/test/e2e/opentelemetry/opentelemetry.test.ts +++ b/test/e2e/opentelemetry/opentelemetry.test.ts @@ -77,80 +77,80 @@ createNextDescribe( await check(async () => { expect(await getSanitizedTraces(1)).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object { - "http.method": "GET", - "http.url": "https://vercel.com/", - "net.peer.name": "vercel.com", - "next.span_name": "fetch GET https://vercel.com/", - "next.span_type": "AppRender.fetch", - }, - "kind": 2, - "name": "fetch GET https://vercel.com/", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.route": "/app/[param]/rsc-fetch", - "next.span_name": "render route (app) /app/[param]/rsc-fetch", - "next.span_type": "AppRender.getBodyResult", - }, - "kind": 0, - "name": "render route (app) /app/[param]/rsc-fetch", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "http.method": "GET", - "http.route": "/app/[param]/rsc-fetch", - "http.status_code": 200, - "http.target": "/app/param/rsc-fetch", - "next.route": "/app/[param]/rsc-fetch", - "next.span_name": "GET /app/[param]/rsc-fetch", - "next.span_type": "BaseServer.handleRequest", - }, - "kind": 1, - "name": "GET /app/[param]/rsc-fetch", - "parentId": undefined, - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.page": "/app/[param]/layout", - "next.span_name": "generateMetadata /app/[param]/layout", - "next.span_type": "ResolveMetadata.generateMetadata", - }, - "kind": 0, - "name": "generateMetadata /app/[param]/layout", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.page": "/app/[param]/rsc-fetch/page", - "next.span_name": "generateMetadata /app/[param]/rsc-fetch/page", - "next.span_type": "ResolveMetadata.generateMetadata", - }, - "kind": 0, - "name": "generateMetadata /app/[param]/rsc-fetch/page", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - ] - `) + Array [ + Object { + "attributes": Object { + "http.method": "GET", + "http.url": "https://vercel.com/", + "net.peer.name": "vercel.com", + "next.span_name": "fetch GET https://vercel.com/", + "next.span_type": "AppRender.fetch", + }, + "kind": 2, + "name": "fetch GET https://vercel.com/", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.route": "/app/[param]/rsc-fetch", + "next.span_name": "render route (app) /app/[param]/rsc-fetch", + "next.span_type": "AppRender.getBodyResult", + }, + "kind": 0, + "name": "render route (app) /app/[param]/rsc-fetch", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "http.method": "GET", + "http.route": "/app/[param]/rsc-fetch", + "http.status_code": 200, + "http.target": "/app/param/rsc-fetch", + "next.route": "/app/[param]/rsc-fetch", + "next.span_name": "GET /app/[param]/rsc-fetch", + "next.span_type": "BaseServer.handleRequest", + }, + "kind": 1, + "name": "GET /app/[param]/rsc-fetch", + "parentId": undefined, + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.page": "/app/[param]/layout", + "next.span_name": "generateMetadata /app/[param]/layout", + "next.span_type": "ResolveMetadata.generateMetadata", + }, + "kind": 0, + "name": "generateMetadata /app/[param]/layout", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.page": "/app/[param]/rsc-fetch/page", + "next.span_name": "generateMetadata /app/[param]/rsc-fetch/page", + "next.span_type": "ResolveMetadata.generateMetadata", + }, + "kind": 0, + "name": "generateMetadata /app/[param]/rsc-fetch/page", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + ] + `) return 'success' }, 'success') }) @@ -160,37 +160,39 @@ createNextDescribe( await check(async () => { expect(await getSanitizedTraces(1)).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object { - "next.route": "/api/app/[param]/data/route", - "next.span_name": "executing api route (app) /api/app/[param]/data/route", - "next.span_type": "AppRouteRouteHandlers.runHandler", - }, - "kind": 0, - "name": "executing api route (app) /api/app/[param]/data/route", - "parentId": "[parent-id]", - "status": Object { - "code": 0, + Array [ + Object { + "attributes": Object { + "next.route": "/api/app/[param]/data/route", + "next.span_name": "executing api route (app) /api/app/[param]/data/route", + "next.span_type": "AppRouteRouteHandlers.runHandler", + }, + "kind": 0, + "name": "executing api route (app) /api/app/[param]/data/route", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, }, - }, - Object { - "attributes": Object { - "http.method": "GET", - "http.status_code": 200, - "http.target": "/api/app/param/data", - "next.span_name": "GET /api/app/param/data", - "next.span_type": "BaseServer.handleRequest", + Object { + "attributes": Object { + "http.method": "GET", + "http.route": "/api/app/[param]/data/route", + "http.status_code": 200, + "http.target": "/api/app/param/data", + "next.route": "/api/app/[param]/data/route", + "next.span_name": "GET /api/app/[param]/data/route", + "next.span_type": "BaseServer.handleRequest", + }, + "kind": 1, + "name": "GET /api/app/[param]/data/route", + "parentId": undefined, + "status": Object { + "code": 0, + }, }, - "kind": 1, - "name": "GET /api/app/param/data", - "parentId": undefined, - "status": Object { - "code": 0, - }, - }, - ] - `) + ] + `) return 'success' }, 'success') }) @@ -202,52 +204,52 @@ createNextDescribe( await check(async () => { expect(await getSanitizedTraces(1)).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object { - "http.method": "GET", - "http.route": "/pages/[param]/getServerSideProps", - "http.status_code": 200, - "http.target": "/pages/param/getServerSideProps", - "next.route": "/pages/[param]/getServerSideProps", - "next.span_name": "GET /pages/[param]/getServerSideProps", - "next.span_type": "BaseServer.handleRequest", - }, - "kind": 1, - "name": "GET /pages/[param]/getServerSideProps", - "parentId": undefined, - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.route": "/pages/[param]/getServerSideProps", - "next.span_name": "getServerSideProps /pages/[param]/getServerSideProps", - "next.span_type": "Render.getServerSideProps", - }, - "kind": 0, - "name": "getServerSideProps /pages/[param]/getServerSideProps", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.route": "/pages/[param]/getServerSideProps", - "next.span_name": "render route (pages) /pages/[param]/getServerSideProps", - "next.span_type": "Render.renderDocument", - }, - "kind": 0, - "name": "render route (pages) /pages/[param]/getServerSideProps", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - ] - `) + Array [ + Object { + "attributes": Object { + "http.method": "GET", + "http.route": "/pages/[param]/getServerSideProps", + "http.status_code": 200, + "http.target": "/pages/param/getServerSideProps", + "next.route": "/pages/[param]/getServerSideProps", + "next.span_name": "GET /pages/[param]/getServerSideProps", + "next.span_type": "BaseServer.handleRequest", + }, + "kind": 1, + "name": "GET /pages/[param]/getServerSideProps", + "parentId": undefined, + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.route": "/pages/[param]/getServerSideProps", + "next.span_name": "getServerSideProps /pages/[param]/getServerSideProps", + "next.span_type": "Render.getServerSideProps", + }, + "kind": 0, + "name": "getServerSideProps /pages/[param]/getServerSideProps", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.route": "/pages/[param]/getServerSideProps", + "next.span_name": "render route (pages) /pages/[param]/getServerSideProps", + "next.span_type": "Render.renderDocument", + }, + "kind": 0, + "name": "render route (pages) /pages/[param]/getServerSideProps", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + ] + `) return 'success' }, 'success') }) @@ -257,52 +259,52 @@ createNextDescribe( await check(async () => { expect(await getSanitizedTraces(1)).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object { - "http.method": "GET", - "http.route": "/pages/[param]/getStaticProps", - "http.status_code": 200, - "http.target": "/pages/param/getStaticProps", - "next.route": "/pages/[param]/getStaticProps", - "next.span_name": "GET /pages/[param]/getStaticProps", - "next.span_type": "BaseServer.handleRequest", - }, - "kind": 1, - "name": "GET /pages/[param]/getStaticProps", - "parentId": undefined, - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.route": "/pages/[param]/getStaticProps", - "next.span_name": "getStaticProps /pages/[param]/getStaticProps", - "next.span_type": "Render.getStaticProps", - }, - "kind": 0, - "name": "getStaticProps /pages/[param]/getStaticProps", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.route": "/pages/[param]/getStaticProps", - "next.span_name": "render route (pages) /pages/[param]/getStaticProps", - "next.span_type": "Render.renderDocument", - }, - "kind": 0, - "name": "render route (pages) /pages/[param]/getStaticProps", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - ] - `) + Array [ + Object { + "attributes": Object { + "http.method": "GET", + "http.route": "/pages/[param]/getStaticProps", + "http.status_code": 200, + "http.target": "/pages/param/getStaticProps", + "next.route": "/pages/[param]/getStaticProps", + "next.span_name": "GET /pages/[param]/getStaticProps", + "next.span_type": "BaseServer.handleRequest", + }, + "kind": 1, + "name": "GET /pages/[param]/getStaticProps", + "parentId": undefined, + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.route": "/pages/[param]/getStaticProps", + "next.span_name": "getStaticProps /pages/[param]/getStaticProps", + "next.span_type": "Render.getStaticProps", + }, + "kind": 0, + "name": "getStaticProps /pages/[param]/getStaticProps", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.route": "/pages/[param]/getStaticProps", + "next.span_name": "render route (pages) /pages/[param]/getStaticProps", + "next.span_type": "Render.renderDocument", + }, + "kind": 0, + "name": "render route (pages) /pages/[param]/getStaticProps", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + ] + `) return 'success' }, 'success') }) @@ -312,38 +314,38 @@ createNextDescribe( await check(async () => { expect(await getSanitizedTraces(1)).toMatchInlineSnapshot(` - Array [ - Object { - "attributes": Object { - "http.method": "GET", - "http.route": "/api/pages/[param]/basic", - "http.status_code": 200, - "http.target": "/api/pages/param/basic", - "next.route": "/api/pages/[param]/basic", - "next.span_name": "GET /api/pages/[param]/basic", - "next.span_type": "BaseServer.handleRequest", - }, - "kind": 1, - "name": "GET /api/pages/[param]/basic", - "parentId": undefined, - "status": Object { - "code": 0, - }, - }, - Object { - "attributes": Object { - "next.span_name": "executing api route (pages) /api/pages/[param]/basic", - "next.span_type": "Node.runHandler", - }, - "kind": 0, - "name": "executing api route (pages) /api/pages/[param]/basic", - "parentId": "[parent-id]", - "status": Object { - "code": 0, - }, - }, - ] - `) + Array [ + Object { + "attributes": Object { + "http.method": "GET", + "http.route": "/api/pages/[param]/basic", + "http.status_code": 200, + "http.target": "/api/pages/param/basic", + "next.route": "/api/pages/[param]/basic", + "next.span_name": "GET /api/pages/[param]/basic", + "next.span_type": "BaseServer.handleRequest", + }, + "kind": 1, + "name": "GET /api/pages/[param]/basic", + "parentId": undefined, + "status": Object { + "code": 0, + }, + }, + Object { + "attributes": Object { + "next.span_name": "executing api route (pages) /api/pages/[param]/basic", + "next.span_type": "Node.runHandler", + }, + "kind": 0, + "name": "executing api route (pages) /api/pages/[param]/basic", + "parentId": "[parent-id]", + "status": Object { + "code": 0, + }, + }, + ] + `) return 'success' }, 'success') }) diff --git a/test/e2e/prerender-native-module.test.ts b/test/e2e/prerender-native-module.test.ts index c23b1d2d05cd0..34adb36ce5bd8 100644 --- a/test/e2e/prerender-native-module.test.ts +++ b/test/e2e/prerender-native-module.test.ts @@ -85,8 +85,6 @@ describe('prerender native module', () => { /node_modules\/sqlite3\/.*?\.node/, /node_modules\/sqlite\/.*?\.js/, /node_modules\/next/, - /next\/router\.js/, - /next\/dist\/client\/router\.js/, /\/data\.sqlite/, ], notTests: [], @@ -99,7 +97,6 @@ describe('prerender native module', () => { ) const { version, files } = JSON.parse(contents) expect(version).toBe(1) - expect( check.tests.every((item) => files.some((file) => item.test(file))) ).toBe(true) diff --git a/test/e2e/prerender.test.ts b/test/e2e/prerender.test.ts index f21b4224fe236..1ff677e583b15 100644 --- a/test/e2e/prerender.test.ts +++ b/test/e2e/prerender.test.ts @@ -2070,7 +2070,6 @@ describe('Prerender', () => { /node_modules\/react\/index\.js/, /node_modules\/react\/package\.json/, /node_modules\/react\/cjs\/react\.production\.min\.js/, - /node_modules\/next/, ], notTests: [], }, @@ -2082,7 +2081,6 @@ describe('Prerender', () => { /node_modules\/react\/index\.js/, /node_modules\/react\/package\.json/, /node_modules\/react\/cjs\/react\.production\.min\.js/, - /node_modules\/next/, /\/world.txt/, ], notTests: [ @@ -2098,9 +2096,6 @@ describe('Prerender', () => { /node_modules\/react\/index\.js/, /node_modules\/react\/package\.json/, /node_modules\/react\/cjs\/react\.production\.min\.js/, - /node_modules\/next/, - /next\/router\.js/, - /next\/dist\/client\/router\.js/, /node_modules\/@firebase\/firestore\/.*?\.js/, ], notTests: [/\/world.txt/], diff --git a/test/integration/externalize-next-server/app/node_modules/comps/index.js b/test/integration/externalize-next-server/app/node_modules/comps/index.js deleted file mode 100644 index 74c3153f1b835..0000000000000 --- a/test/integration/externalize-next-server/app/node_modules/comps/index.js +++ /dev/null @@ -1,5 +0,0 @@ -const react = require('react') - -module.exports = function() { - return react.createElement('p', null, 'MyComp:', typeof window) -} diff --git a/test/integration/externalize-next-server/app/node_modules/comps/package.json b/test/integration/externalize-next-server/app/node_modules/comps/package.json deleted file mode 100644 index 6e665b646a6ad..0000000000000 --- a/test/integration/externalize-next-server/app/node_modules/comps/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "comps", - "version": "1.0.0", - "main": "index.js", - "license": "MIT" -} diff --git a/test/integration/externalize-next-server/app/package.json b/test/integration/externalize-next-server/app/package.json deleted file mode 100644 index c5bd706a3a950..0000000000000 --- a/test/integration/externalize-next-server/app/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "externalize-next-server-app", - "version": "1.0.0", - "main": "index.js", - "license": "MIT" -} diff --git a/test/integration/externalize-next-server/app/pages/index.js b/test/integration/externalize-next-server/app/pages/index.js deleted file mode 100644 index 9ceb7bee3db17..0000000000000 --- a/test/integration/externalize-next-server/app/pages/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import MyComp from 'comps' - -const Page = () => ( - <> -

Hello {typeof window}

- - -) - -Page.getInitialProps = () => ({}) - -export default Page diff --git a/test/integration/externalize-next-server/test/index.test.js b/test/integration/externalize-next-server/test/index.test.js deleted file mode 100644 index bba968de16585..0000000000000 --- a/test/integration/externalize-next-server/test/index.test.js +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-env jest */ -import path from 'path' -import { nextBuild, readNextBuildServerPageFile } from 'next-test-utils' - -const appDir = path.join(__dirname, '../app') - -describe('externalize next/dist/shared', () => { - beforeAll(async () => { - await nextBuild(appDir) - }) - - it('Bundle next/dist/shared/lib/head.js but not next/dist/shared/lib/head-manager-context.js in _error', async () => { - const content = readNextBuildServerPageFile(appDir, '/_error') - expect(content).toContain( - `require("next/dist/shared/lib/head-manager-context.js")` - ) - expect(content).not.toContain(`require("next/dist/shared/lib/head.js")`) - }) -}) diff --git a/test/integration/jsconfig-baseurl/test/index.test.js b/test/integration/jsconfig-baseurl/test/index.test.js index 91f084ce1d55e..4ad014d0e8f03 100644 --- a/test/integration/jsconfig-baseurl/test/index.test.js +++ b/test/integration/jsconfig-baseurl/test/index.test.js @@ -72,12 +72,6 @@ describe('TypeScript Features', () => { const helloTrace = await fs.readJSON( join(appDir, '.next/server/pages/hello.js.nft.json') ) - const appTrace = await fs.readJSON( - join(appDir, '.next/server/pages/_app.js.nft.json') - ) - expect( - appTrace.files.some((file) => file.includes('node_modules/next')) - ).toBe(true) expect( helloTrace.files.some((file) => file.includes('components/world.js')) ).toBe(false) diff --git a/test/integration/jsconfig-paths/test/index.test.js b/test/integration/jsconfig-paths/test/index.test.js index 77c84e7edfbb4..bc3c635b9ce96 100644 --- a/test/integration/jsconfig-paths/test/index.test.js +++ b/test/integration/jsconfig-paths/test/index.test.js @@ -89,9 +89,6 @@ function runTests() { await nextBuild(appDir) }) it('should trace correctly', async () => { - const appTrace = await fs.readJSON( - join(appDir, '.next/server/pages/_app.js.nft.json') - ) const singleAliasTrace = await fs.readJSON( join(appDir, '.next/server/pages/single-alias.js.nft.json') ) @@ -107,9 +104,7 @@ function runTests() { const basicAliasTrace = await fs.readJSON( join(appDir, '.next/server/pages/basic-alias.js.nft.json') ) - expect( - appTrace.files.some((file) => file.includes('node_modules/next')) - ).toBe(true) + expect( singleAliasTrace.files.some((file) => file.includes('components/hello.js')