next_core/next_app/
app_route_entry.rs1use anyhow::Result;
2use turbo_rcstr::{RcStr, rcstr};
3use turbo_tasks::{ResolvedVc, Vc, fxindexmap};
4use turbo_tasks_fs::FileSystemPath;
5use turbopack::ModuleAssetContext;
6use turbopack_core::{
7 context::AssetContext,
8 module::Module,
9 reference_type::{EntryReferenceSubType, ReferenceType},
10 source::Source,
11};
12
13use crate::{
14 app_segment_config::NextSegmentConfig,
15 next_app::{AppEntry, AppPage, AppPath},
16 next_config::{NextConfig, OutputType},
17 next_edge::entry::wrap_edge_entry,
18 parse_segment_config_from_source,
19 util::{NextRuntime, app_function_name, load_next_js_template},
20};
21
22#[turbo_tasks::function]
30pub async fn get_app_route_entry(
31 nodejs_context: Vc<ModuleAssetContext>,
32 edge_context: Vc<ModuleAssetContext>,
33 source: Vc<Box<dyn Source>>,
34 page: AppPage,
35 project_root: FileSystemPath,
36 original_segment_config: Option<Vc<NextSegmentConfig>>,
37 next_config: Vc<NextConfig>,
38) -> Result<Vc<AppEntry>> {
39 let segment_from_source = parse_segment_config_from_source(source);
40 let config = if let Some(original_segment_config) = original_segment_config {
41 let mut segment_config = segment_from_source.owned().await?;
42 segment_config.apply_parent_config(&*original_segment_config.await?);
43 segment_config.into()
44 } else {
45 segment_from_source
46 };
47
48 let is_edge = matches!(config.await?.runtime, Some(NextRuntime::Edge));
49 let module_asset_context = if is_edge {
50 edge_context
51 } else {
52 nodejs_context
53 };
54
55 let original_name: RcStr = page.to_string().into();
56 let pathname: RcStr = AppPath::from(page.clone()).to_string().into();
57
58 let path = source.ident().path().owned().await?;
59
60 let inner = rcstr!("INNER_APP_ROUTE");
61
62 let output_type: &str = next_config
63 .await?
64 .output
65 .as_ref()
66 .map(|o| match o {
67 OutputType::Standalone => "\"standalone\"",
68 OutputType::Export => "\"export\"",
69 })
70 .unwrap_or("\"\"");
71
72 let virtual_source = load_next_js_template(
74 "app-route.js",
75 project_root.clone(),
76 &[
77 ("VAR_DEFINITION_PAGE", &*page.to_string()),
78 ("VAR_DEFINITION_PATHNAME", &pathname),
79 ("VAR_DEFINITION_FILENAME", path.file_stem().unwrap()),
80 ("VAR_DEFINITION_BUNDLE_PATH", ""),
82 ("VAR_RESOLVED_PAGE_PATH", &path.value_to_string().await?),
83 ("VAR_USERLAND", &inner),
84 ],
85 &[("nextConfigOutput", output_type)],
86 &[],
87 )
88 .await?;
89
90 let userland_module = module_asset_context
91 .process(
92 source,
93 ReferenceType::Entry(EntryReferenceSubType::AppRoute),
94 )
95 .module()
96 .to_resolved()
97 .await?;
98
99 let inner_assets = fxindexmap! {
100 inner => userland_module
101 };
102
103 let mut rsc_entry = module_asset_context
104 .process(
105 Vc::upcast(virtual_source),
106 ReferenceType::Internal(ResolvedVc::cell(inner_assets)),
107 )
108 .module();
109
110 if is_edge {
111 rsc_entry = wrap_edge_route(
112 Vc::upcast(module_asset_context),
113 project_root,
114 rsc_entry,
115 page,
116 next_config,
117 );
118 }
119
120 Ok(AppEntry {
121 pathname,
122 original_name,
123 rsc_entry: rsc_entry.to_resolved().await?,
124 config: config.to_resolved().await?,
125 }
126 .cell())
127}
128
129#[turbo_tasks::function]
130async fn wrap_edge_route(
131 asset_context: Vc<Box<dyn AssetContext>>,
132 project_root: FileSystemPath,
133 entry: ResolvedVc<Box<dyn Module>>,
134 page: AppPage,
135 next_config: Vc<NextConfig>,
136) -> Result<Vc<Box<dyn Module>>> {
137 let inner = rcstr!("INNER_ROUTE_ENTRY");
138
139 let next_config = &*next_config.await?;
140
141 let source = load_next_js_template(
142 "edge-app-route.js",
143 project_root.clone(),
144 &[("VAR_USERLAND", &*inner), ("VAR_PAGE", &page.to_string())],
145 &[("nextConfig", &*serde_json::to_string(next_config)?)],
146 &[],
147 )
148 .await?;
149
150 let inner_assets = fxindexmap! {
151 inner => entry
152 };
153
154 let wrapped = asset_context
155 .process(
156 Vc::upcast(source),
157 ReferenceType::Internal(ResolvedVc::cell(inner_assets)),
158 )
159 .module();
160
161 Ok(wrap_edge_entry(
162 asset_context,
163 project_root.clone(),
164 wrapped,
165 app_function_name(&page).into(),
166 ))
167}