Split into dev server and next.rs api mode (#53220)
### What? * split into two dev modes * split app-index into webpack and turbopack * add two different entrypoints ### Why? ### How?
This commit is contained in:
parent
a1adaf89ef
commit
39fd9177ef
21 changed files with 180 additions and 108 deletions
|
@ -3,4 +3,10 @@
|
|||
*/
|
||||
|
||||
import '../shims'
|
||||
import 'next/dist/client/app-next'
|
||||
import { appBootstrap } from 'next/dist/client/app-bootstrap'
|
||||
|
||||
appBootstrap(() => {
|
||||
require('./app-turbopack')
|
||||
const { hydrate } = require('./app-index')
|
||||
hydrate()
|
||||
})
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
// @ts-expect-error
|
||||
process.env.__NEXT_NEW_LINK_BEHAVIOR = true
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
self.__next_require__ = __turbopack_require__
|
||||
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line no-undef
|
||||
;(self as any).__next_chunk_load__ = __turbopack_load__
|
|
@ -133,7 +133,7 @@ async fn next_client_transition(
|
|||
next_config: Vc<NextConfig>,
|
||||
) -> Result<Vc<Box<dyn Transition>>> {
|
||||
let ty: Value<ClientContextType> = Value::new(ClientContextType::App { app_dir });
|
||||
let mode = NextMode::Development;
|
||||
let mode = NextMode::DevServer;
|
||||
let client_module_options_context = get_client_module_options_context(
|
||||
project_path,
|
||||
execution_context,
|
||||
|
@ -170,7 +170,7 @@ fn next_ssr_client_module_transition(
|
|||
server_addr: Vc<ServerAddr>,
|
||||
) -> Vc<Box<dyn Transition>> {
|
||||
let ty = Value::new(ServerContextType::AppSSR { app_dir });
|
||||
let mode = NextMode::Development;
|
||||
let mode = NextMode::DevServer;
|
||||
Vc::upcast(
|
||||
NextSSRClientModuleTransition {
|
||||
ssr_module_options_context: get_server_module_options_context(
|
||||
|
@ -202,7 +202,7 @@ fn next_edge_ssr_client_module_transition(
|
|||
server_addr: Vc<ServerAddr>,
|
||||
) -> Vc<Box<dyn Transition>> {
|
||||
let ty = Value::new(ServerContextType::AppSSR { app_dir });
|
||||
let mode = NextMode::Development;
|
||||
let mode = NextMode::DevServer;
|
||||
Vc::upcast(
|
||||
NextSSRClientModuleTransition {
|
||||
ssr_module_options_context: get_server_module_options_context(
|
||||
|
@ -306,7 +306,7 @@ fn next_edge_route_transition(
|
|||
output_path: Vc<FileSystemPath>,
|
||||
execution_context: Vc<ExecutionContext>,
|
||||
) -> Vc<Box<dyn Transition>> {
|
||||
let mode = NextMode::Development;
|
||||
let mode = NextMode::DevServer;
|
||||
let server_ty = Value::new(ServerContextType::AppRoute { app_dir });
|
||||
|
||||
let edge_compile_time_info = get_edge_compile_time_info(project_path, server_addr);
|
||||
|
@ -570,7 +570,7 @@ pub async fn create_app_source(
|
|||
client_chunking_context,
|
||||
client_compile_time_info,
|
||||
true,
|
||||
NextMode::Development,
|
||||
NextMode::DevServer,
|
||||
next_config,
|
||||
server_addr,
|
||||
output_path,
|
||||
|
@ -584,7 +584,7 @@ pub async fn create_app_source(
|
|||
client_chunking_context,
|
||||
client_compile_time_info,
|
||||
false,
|
||||
NextMode::Development,
|
||||
NextMode::DevServer,
|
||||
next_config,
|
||||
server_addr,
|
||||
output_path,
|
||||
|
@ -905,7 +905,7 @@ impl AppRenderer {
|
|||
loader_tree,
|
||||
context,
|
||||
ServerComponentTransition::TransitionName(rsc_transition.to_string()),
|
||||
NextMode::Development,
|
||||
NextMode::DevServer,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ pub async fn get_fallback_page(
|
|||
next_config: Vc<NextConfig>,
|
||||
) -> Result<Vc<DevHtmlAsset>> {
|
||||
let ty = Value::new(ClientContextType::Fallback);
|
||||
let mode = NextMode::Development;
|
||||
let mode = NextMode::DevServer;
|
||||
let resolve_options_context =
|
||||
get_client_resolve_options_context(project_path, ty, mode, next_config, execution_context);
|
||||
let module_options_context = get_client_module_options_context(
|
||||
|
|
|
@ -107,7 +107,7 @@ impl LoaderTreeBuilder {
|
|||
let identifier = magic_identifier::mangle(&format!("{name} #{i}"));
|
||||
|
||||
match self.mode {
|
||||
NextMode::Development => {
|
||||
NextMode::Development | NextMode::DevServer => {
|
||||
let chunks_identifier =
|
||||
magic_identifier::mangle(&format!("chunks of {name} #{i}"));
|
||||
writeln!(
|
||||
|
|
|
@ -18,7 +18,9 @@ use turbo_tasks::{debug::ValueDebugFormat, trace::TraceRawVcs, TaskInput};
|
|||
ValueDebugFormat,
|
||||
)]
|
||||
pub enum NextMode {
|
||||
/// `next dev`
|
||||
/// `next dev --turbo`
|
||||
DevServer,
|
||||
/// `next dev --experimental-turbo`
|
||||
Development,
|
||||
/// `next build`
|
||||
Build,
|
||||
|
@ -28,7 +30,7 @@ impl NextMode {
|
|||
/// Returns the NODE_ENV value for the current mode.
|
||||
pub fn node_env(&self) -> &'static str {
|
||||
match self {
|
||||
NextMode::Development => "development",
|
||||
NextMode::Development | NextMode::DevServer => "development",
|
||||
NextMode::Build => "production",
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +38,7 @@ impl NextMode {
|
|||
/// Returns true if the development React runtime should be used.
|
||||
pub fn is_react_development(&self) -> bool {
|
||||
match self {
|
||||
NextMode::Development => true,
|
||||
NextMode::Development | NextMode::DevServer => true,
|
||||
NextMode::Build => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,7 +291,10 @@ pub fn get_client_chunking_context(
|
|||
);
|
||||
|
||||
let builder = match mode {
|
||||
NextMode::Development => builder.hot_module_replacement(),
|
||||
NextMode::DevServer => builder.hot_module_replacement(),
|
||||
NextMode::Development => builder
|
||||
.hot_module_replacement()
|
||||
.chunk_base_path(Vc::cell(Some("_next/".to_string()))),
|
||||
NextMode::Build => builder.chunk_base_path(Vc::cell(Some("_next/".to_string()))),
|
||||
};
|
||||
|
||||
|
@ -328,6 +331,27 @@ pub async fn get_client_runtime_entries(
|
|||
}
|
||||
|
||||
match mode {
|
||||
NextMode::DevServer => {
|
||||
let resolve_options_context = get_client_resolve_options_context(
|
||||
project_root,
|
||||
ty,
|
||||
mode,
|
||||
next_config,
|
||||
execution_context,
|
||||
);
|
||||
let enable_react_refresh =
|
||||
assert_can_resolve_react_refresh(project_root, resolve_options_context)
|
||||
.await?
|
||||
.as_request();
|
||||
|
||||
// It's important that React Refresh come before the regular bootstrap file,
|
||||
// because the bootstrap contains JSX which requires Refresh's global
|
||||
// functions to be available.
|
||||
if let Some(request) = enable_react_refresh {
|
||||
runtime_entries
|
||||
.push(RuntimeEntry::Request(request, project_root.join("_".to_string())).cell())
|
||||
};
|
||||
}
|
||||
NextMode::Development => {
|
||||
let resolve_options_context = get_client_resolve_options_context(
|
||||
project_root,
|
||||
|
@ -348,6 +372,18 @@ pub async fn get_client_runtime_entries(
|
|||
runtime_entries
|
||||
.push(RuntimeEntry::Request(request, project_root.join("_".to_string())).cell())
|
||||
};
|
||||
|
||||
if matches!(*ty, ClientContextType::App { .. },) {
|
||||
runtime_entries.push(
|
||||
RuntimeEntry::Request(
|
||||
Request::parse(Value::new(Pattern::Constant(
|
||||
"next/dist/client/app-next-dev-turbopack.js".to_string(),
|
||||
))),
|
||||
project_root.join("_".to_string()),
|
||||
)
|
||||
.cell(),
|
||||
);
|
||||
}
|
||||
}
|
||||
NextMode::Build => match *ty {
|
||||
ClientContextType::App { .. } => {
|
||||
|
|
|
@ -391,7 +391,7 @@ pub async fn insert_next_server_special_aliases(
|
|||
// In development, we *always* use the bundled version of React, even in
|
||||
// SSR, since we're bundling Next.js alongside it.
|
||||
(
|
||||
NextMode::Development,
|
||||
NextMode::DevServer,
|
||||
ServerContextType::AppSSR { app_dir }
|
||||
| ServerContextType::AppRSC { app_dir, .. }
|
||||
| ServerContextType::AppRoute { app_dir },
|
||||
|
@ -429,7 +429,7 @@ pub async fn insert_next_server_special_aliases(
|
|||
// NOTE(alexkirsz) This logic maps loosely to
|
||||
// `next.js/packages/next/src/build/webpack-config.ts`, where:
|
||||
//
|
||||
// ## RSC (Build)
|
||||
// ## RSC
|
||||
//
|
||||
// * always bundles
|
||||
// * maps react -> react/shared-subset (through the "react-server" exports condition)
|
||||
|
@ -437,7 +437,7 @@ pub 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::Build | NextMode::Development,
|
||||
ServerContextType::AppRSC { app_dir, .. } | ServerContextType::AppRoute { app_dir },
|
||||
) => {
|
||||
import_map.insert_exact_alias(
|
||||
|
@ -471,14 +471,14 @@ pub async fn insert_next_server_special_aliases(
|
|||
);
|
||||
}
|
||||
}
|
||||
// ## SSR (Build)
|
||||
// ## SSR
|
||||
//
|
||||
// * always uses externals, to ensure we're using the same React instance as the Next.js
|
||||
// runtime
|
||||
// * maps react-dom -> react-dom/server-rendering-stub
|
||||
// * passes through react and (react|react-dom|react-server-dom-webpack)/(.*) to
|
||||
// next/dist/compiled/react and next/dist/compiled/$1/$2 resp.
|
||||
(NextMode::Build, ServerContextType::AppSSR { .. }) => {
|
||||
(NextMode::Build | NextMode::Development, ServerContextType::AppSSR { .. }) => {
|
||||
import_map.insert_exact_alias(
|
||||
"react",
|
||||
external_request_to_import_mapping("next/dist/compiled/react"),
|
||||
|
|
|
@ -643,6 +643,7 @@ pub fn get_server_runtime_entries(
|
|||
|
||||
match mode {
|
||||
NextMode::Development => {}
|
||||
NextMode::DevServer => {}
|
||||
NextMode::Build => {
|
||||
if let ServerContextType::AppRSC { .. } = ty.into_value() {
|
||||
runtime_entries.push(
|
||||
|
|
|
@ -60,14 +60,14 @@ impl CustomTransformer for NextJsDynamic {
|
|||
let p = std::mem::replace(program, Program::Module(Module::dummy()));
|
||||
*program = p.fold_with(&mut next_dynamic(
|
||||
match self.mode {
|
||||
NextMode::Development => true,
|
||||
NextMode::Development | NextMode::DevServer => true,
|
||||
NextMode::Build => false,
|
||||
},
|
||||
self.is_server,
|
||||
self.is_server_components,
|
||||
NextDynamicMode::Turbopack {
|
||||
dynamic_transition_name: match self.mode {
|
||||
NextMode::Development => "next-client-chunks".to_string(),
|
||||
NextMode::Development | NextMode::DevServer => "next-client-chunks".to_string(),
|
||||
NextMode::Build => "next-dynamic".to_string(),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -96,7 +96,7 @@ pub async fn create_page_source(
|
|||
project_root.join("pages".to_string())
|
||||
};
|
||||
|
||||
let mode = NextMode::Development;
|
||||
let mode = NextMode::DevServer;
|
||||
let client_ty = Value::new(ClientContextType::Pages { pages_dir });
|
||||
let server_ty = Value::new(ServerContextType::Pages { pages_dir });
|
||||
let server_data_ty = Value::new(ServerContextType::PagesData { pages_dir });
|
||||
|
@ -344,7 +344,7 @@ async fn create_page_source_for_file(
|
|||
node_root: Vc<FileSystemPath>,
|
||||
render_data: Vc<JsonValue>,
|
||||
) -> Result<Vc<Box<dyn ContentSource>>> {
|
||||
let mode = NextMode::Development;
|
||||
let mode = NextMode::DevServer;
|
||||
|
||||
let server_chunking_context = Vc::upcast(
|
||||
DevChunkingContext::builder(
|
||||
|
|
|
@ -231,7 +231,7 @@ fn edge_transition_map(
|
|||
next_config: Vc<NextConfig>,
|
||||
execution_context: Vc<ExecutionContext>,
|
||||
) -> Vc<TransitionsByName> {
|
||||
let mode = NextMode::Development;
|
||||
let mode = NextMode::DevServer;
|
||||
|
||||
let edge_compile_time_info = get_edge_compile_time_info(project_path, server_addr);
|
||||
|
||||
|
|
|
@ -175,7 +175,7 @@ pub async fn create_web_entry_source(
|
|||
next_config: Vc<NextConfig>,
|
||||
) -> Result<Vc<Box<dyn ContentSource>>> {
|
||||
let ty = Value::new(ClientContextType::Other);
|
||||
let mode = NextMode::Development;
|
||||
let mode = NextMode::DevServer;
|
||||
let compile_time_info = get_compile_time_info(browserslist_query);
|
||||
let context = get_web_client_asset_context(
|
||||
project_root,
|
||||
|
|
|
@ -337,7 +337,7 @@ async fn source(
|
|||
let execution_context =
|
||||
ExecutionContext::new(project_path, Vc::upcast(build_chunking_context), env);
|
||||
|
||||
let mode = NextMode::Development;
|
||||
let mode = NextMode::DevServer;
|
||||
let next_config_execution_context = execution_context.with_layer("next_config".to_string());
|
||||
let next_config = load_next_config(next_config_execution_context);
|
||||
let rewrites = load_rewrites(next_config_execution_context);
|
||||
|
|
|
@ -31,88 +31,6 @@ window.addEventListener('error', (ev: WindowEventMap['error']): void => {
|
|||
|
||||
/// <reference types="react-dom/experimental" />
|
||||
|
||||
// Override chunk URL mapping in the webpack runtime
|
||||
// https://github.com/webpack/webpack/blob/2738eebc7880835d88c727d364ad37f3ec557593/lib/RuntimeGlobals.js#L204
|
||||
|
||||
declare global {
|
||||
const __webpack_require__: any
|
||||
}
|
||||
|
||||
const addChunkSuffix =
|
||||
(getOriginalChunk: (chunkId: any) => string) => (chunkId: any) => {
|
||||
return (
|
||||
getOriginalChunk(chunkId) +
|
||||
`${
|
||||
process.env.NEXT_DEPLOYMENT_ID
|
||||
? `?dpl=${process.env.NEXT_DEPLOYMENT_ID}`
|
||||
: ''
|
||||
}`
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const getChunkScriptFilename = __webpack_require__.u
|
||||
const chunkFilenameMap: any = {}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_require__.u = addChunkSuffix((chunkId) =>
|
||||
encodeURI(chunkFilenameMap[chunkId] || getChunkScriptFilename(chunkId))
|
||||
)
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const getChunkCssFilename = __webpack_require__.k
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_require__.k = addChunkSuffix(getChunkCssFilename)
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const getMiniCssFilename = __webpack_require__.miniCssF
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_require__.miniCssF = addChunkSuffix(getMiniCssFilename)
|
||||
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line no-undef
|
||||
if (process.turbopack) {
|
||||
// eslint-disable-next-line no-undef
|
||||
// @ts-expect-error TODO: fix type
|
||||
self.__next_require__ = __turbopack_require__
|
||||
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line no-undef
|
||||
;(self as any).__next_chunk_load__ = __turbopack_load__
|
||||
} else {
|
||||
// Ignore the module ID transform in client.
|
||||
// eslint-disable-next-line no-undef
|
||||
// @ts-expect-error TODO: fix type
|
||||
self.__next_require__ =
|
||||
process.env.NODE_ENV !== 'production'
|
||||
? (id: string) => {
|
||||
const mod = __webpack_require__(id)
|
||||
if (typeof mod === 'object') {
|
||||
// Return a proxy to flight client to make sure it's always getting
|
||||
// the latest module, instead of being cached.
|
||||
return new Proxy(mod, {
|
||||
get(_target, prop) {
|
||||
return __webpack_require__(id)[prop]
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return mod
|
||||
}
|
||||
: __webpack_require__
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
;(self as any).__next_chunk_load__ = (chunk: string) => {
|
||||
if (!chunk) return Promise.resolve()
|
||||
const [chunkId, chunkFilePath] = chunk.split(':')
|
||||
chunkFilenameMap[chunkId] = chunkFilePath
|
||||
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line no-undef
|
||||
return __webpack_chunk_load__(chunkId)
|
||||
}
|
||||
}
|
||||
|
||||
const appElement: HTMLElement | Document | null = document
|
||||
|
||||
const getCacheKey = () => {
|
||||
|
|
13
packages/next/src/client/app-next-dev-turbopack.ts
Normal file
13
packages/next/src/client/app-next-dev-turbopack.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
// TODO-APP: hydration warning
|
||||
|
||||
import { appBootstrap } from './app-bootstrap'
|
||||
|
||||
window.next.version += '-turbo'
|
||||
|
||||
appBootstrap(() => {
|
||||
require('./app-turbopack')
|
||||
const { hydrate } = require('./app-index')
|
||||
hydrate()
|
||||
})
|
||||
|
||||
// TODO-APP: build indicator
|
|
@ -3,6 +3,7 @@
|
|||
import { appBootstrap } from './app-bootstrap'
|
||||
|
||||
appBootstrap(() => {
|
||||
require('./app-webpack')
|
||||
const { hydrate } = require('./app-index')
|
||||
hydrate()
|
||||
})
|
||||
|
|
|
@ -4,6 +4,7 @@ appBootstrap(() => {
|
|||
// Include app-router and layout-router in the main chunk
|
||||
require('next/dist/client/components/app-router')
|
||||
require('next/dist/client/components/layout-router')
|
||||
require('./app-webpack')
|
||||
const { hydrate } = require('./app-index')
|
||||
hydrate()
|
||||
})
|
||||
|
|
13
packages/next/src/client/app-turbopack.ts
Normal file
13
packages/next/src/client/app-turbopack.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
declare let __turbopack_require__: any
|
||||
|
||||
// @ts-expect-error
|
||||
process.env.__NEXT_NEW_LINK_BEHAVIOR = true
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
;(self as any).__next_require__ = __turbopack_require__
|
||||
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line no-undef
|
||||
;(self as any).__next_chunk_load__ = __turbopack_load__
|
||||
|
||||
export {}
|
69
packages/next/src/client/app-webpack.ts
Normal file
69
packages/next/src/client/app-webpack.ts
Normal file
|
@ -0,0 +1,69 @@
|
|||
// Override chunk URL mapping in the webpack runtime
|
||||
// https://github.com/webpack/webpack/blob/2738eebc7880835d88c727d364ad37f3ec557593/lib/RuntimeGlobals.js#L204
|
||||
|
||||
declare const __webpack_require__: any
|
||||
|
||||
const addChunkSuffix =
|
||||
(getOriginalChunk: (chunkId: any) => string) => (chunkId: any) => {
|
||||
return (
|
||||
getOriginalChunk(chunkId) +
|
||||
`${
|
||||
process.env.NEXT_DEPLOYMENT_ID
|
||||
? `?dpl=${process.env.NEXT_DEPLOYMENT_ID}`
|
||||
: ''
|
||||
}`
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const getChunkScriptFilename = __webpack_require__.u
|
||||
const chunkFilenameMap: any = {}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_require__.u = addChunkSuffix((chunkId) =>
|
||||
encodeURI(chunkFilenameMap[chunkId] || getChunkScriptFilename(chunkId))
|
||||
)
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const getChunkCssFilename = __webpack_require__.k
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_require__.k = addChunkSuffix(getChunkCssFilename)
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const getMiniCssFilename = __webpack_require__.miniCssF
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_require__.miniCssF = addChunkSuffix(getMiniCssFilename)
|
||||
|
||||
// Ignore the module ID transform in client.
|
||||
// eslint-disable-next-line no-undef
|
||||
// @ts-expect-error TODO: fix type
|
||||
self.__next_require__ =
|
||||
process.env.NODE_ENV !== 'production'
|
||||
? (id: string) => {
|
||||
const mod = __webpack_require__(id)
|
||||
if (typeof mod === 'object') {
|
||||
// Return a proxy to flight client to make sure it's always getting
|
||||
// the latest module, instead of being cached.
|
||||
return new Proxy(mod, {
|
||||
get(_target, prop) {
|
||||
return __webpack_require__(id)[prop]
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return mod
|
||||
}
|
||||
: __webpack_require__
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
;(self as any).__next_chunk_load__ = (chunk: string) => {
|
||||
if (!chunk) return Promise.resolve()
|
||||
const [chunkId, chunkFilePath] = chunk.split(':')
|
||||
chunkFilenameMap[chunkId] = chunkFilePath
|
||||
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line no-undef
|
||||
return __webpack_chunk_load__(chunkId)
|
||||
}
|
||||
|
||||
export {}
|
|
@ -1,3 +1,4 @@
|
|||
declare const __webpack_require__: any
|
||||
declare let __webpack_public_path__: string
|
||||
|
||||
const addChunkSuffix =
|
||||
|
@ -34,3 +35,5 @@ __webpack_require__.miniCssF = addChunkSuffix(getMiniCssFilename)
|
|||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
__webpack_public_path__ = path
|
||||
}
|
||||
|
||||
export {}
|
||||
|
|
Loading…
Reference in a new issue