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 next_app::{AppEntry, AppPage, AppPath},
15 next_config::{NextConfig, OutputType},
16 next_edge::entry::wrap_edge_entry,
17 parse_segment_config_from_source,
18 segment_config::{NextSegmentConfig, ParseSegmentMode},
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, ParseSegmentMode::App);
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.cell()
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 .output()
64 .await?
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 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 );
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_route(
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 let inner = rcstr!("INNER_ROUTE_ENTRY");
136
137 let source = load_next_js_template(
138 "edge-app-route.js",
139 project_root.clone(),
140 [("VAR_USERLAND", &*inner), ("VAR_PAGE", &page.to_string())],
141 [],
142 [],
143 )
144 .await?;
145
146 let inner_assets = fxindexmap! {
147 inner => 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.clone(),
160 wrapped,
161 app_function_name(&page).into(),
162 ))
163}