1use std::io::Write;
2
3use anyhow::Result;
4use turbo_rcstr::RcStr;
5use turbo_tasks::{ResolvedVc, Vc, fxindexmap};
6use turbo_tasks_fs::{self, File, FileContent, FileSystemPath, rope::RopeBuilder};
7use turbopack::ModuleAssetContext;
8use turbopack_core::{
9 asset::{Asset, AssetContent},
10 context::AssetContext,
11 module::Module,
12 reference_type::ReferenceType,
13 source::Source,
14 virtual_source::VirtualSource,
15};
16use turbopack_ecmascript::runtime_functions::{TURBOPACK_LOAD, TURBOPACK_REQUIRE};
17
18use crate::{
19 app_page_loader_tree::AppPageLoaderTreeModule,
20 app_structure::AppPageLoaderTree,
21 next_app::{AppPage, AppPath, app_entry::AppEntry},
22 next_config::NextConfig,
23 next_edge::entry::wrap_edge_entry,
24 next_server_component::NextServerComponentTransition,
25 parse_segment_config_from_loader_tree,
26 util::{NextRuntime, app_function_name, file_content_rope, load_next_js_template},
27};
28
29#[turbo_tasks::function]
31pub async fn get_app_page_entry(
32 nodejs_context: ResolvedVc<ModuleAssetContext>,
33 edge_context: ResolvedVc<ModuleAssetContext>,
34 loader_tree: Vc<AppPageLoaderTree>,
35 page: AppPage,
36 project_root: FileSystemPath,
37 next_config: Vc<NextConfig>,
38) -> Result<Vc<AppEntry>> {
39 let config = parse_segment_config_from_loader_tree(loader_tree);
40 let is_edge = matches!(config.await?.runtime, Some(NextRuntime::Edge));
41 let module_asset_context = if is_edge {
42 edge_context
43 } else {
44 nodejs_context
45 };
46
47 let server_component_transition =
48 ResolvedVc::upcast(NextServerComponentTransition::new().to_resolved().await?);
49
50 let base_path = next_config.base_path().owned().await?;
51 let loader_tree = AppPageLoaderTreeModule::build(
52 loader_tree,
53 module_asset_context,
54 server_component_transition,
55 base_path,
56 )
57 .await?;
58
59 let AppPageLoaderTreeModule {
60 inner_assets,
61 imports,
62 loader_tree_code,
63 } = loader_tree;
64
65 let mut result = RopeBuilder::default();
66
67 for import in imports {
68 writeln!(result, "{import}")?;
69 }
70
71 let original_name: RcStr = page.to_string().into();
72 let pathname: RcStr = AppPath::from(page.clone()).to_string().into();
73
74 let source = load_next_js_template(
76 "app-page.js",
77 project_root.clone(),
78 [
79 ("VAR_DEFINITION_PAGE", &*page.to_string()),
80 ("VAR_DEFINITION_PATHNAME", &pathname),
81 ],
82 [
83 ("tree", &*loader_tree_code),
84 ("__next_app_require__", &TURBOPACK_REQUIRE.bound()),
85 ("__next_app_load_chunk__", &TURBOPACK_LOAD.bound()),
86 ],
87 [],
88 )
89 .await?;
90
91 let source_content = &*file_content_rope(source.content().file_content()).await?;
92
93 result.concat(source_content);
94
95 let query = qstring::QString::new(vec![("page", page.to_string())]);
96
97 let file = File::from(result.build());
98 let source = VirtualSource::new_with_ident(
99 source.ident().with_query(RcStr::from(format!("?{query}"))),
100 AssetContent::file(FileContent::Content(file).cell()),
101 );
102
103 let mut rsc_entry = module_asset_context
104 .process(
105 Vc::upcast(source),
106 ReferenceType::Internal(ResolvedVc::cell(inner_assets)),
107 )
108 .module();
109
110 if is_edge {
111 rsc_entry = wrap_edge_page(
112 *ResolvedVc::upcast(module_asset_context),
113 project_root.clone(),
114 rsc_entry,
115 page,
116 );
117 };
118
119 Ok(AppEntry {
120 pathname,
121 original_name,
122 rsc_entry: rsc_entry.to_resolved().await?,
123 config: config.to_resolved().await?,
124 }
125 .cell())
126}
127
128#[turbo_tasks::function]
129async fn wrap_edge_page(
130 asset_context: Vc<Box<dyn AssetContext>>,
131 project_root: FileSystemPath,
132 entry: ResolvedVc<Box<dyn Module>>,
133 page: AppPage,
134) -> Result<Vc<Box<dyn Module>>> {
135 const INNER: &str = "INNER_PAGE_ENTRY";
136
137 let source = load_next_js_template(
138 "edge-ssr-app.js",
139 project_root.clone(),
140 [("VAR_USERLAND", INNER), ("VAR_PAGE", &page.to_string())],
141 [],
142 [("incrementalCacheHandler", None)],
143 )
144 .await?;
145
146 let inner_assets = fxindexmap! {
147 INNER.into() => entry
148 };
149
150 let wrapped = asset_context
151 .process(
152 source,
153 ReferenceType::Internal(ResolvedVc::cell(inner_assets)),
154 )
155 .module();
156
157 Ok(wrap_edge_entry(
158 asset_context,
159 project_root,
160 wrapped,
161 app_function_name(&page).into(),
162 ))
163}