Skip to content

Commit 19d0034

Browse files
feat: port next.js template loading logic
1 parent ad8601d commit 19d0034

File tree

5 files changed

+313
-182
lines changed

5 files changed

+313
-182
lines changed

packages/next-swc/crates/next-core/src/middleware.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
use anyhow::Result;
22
use indexmap::indexmap;
33
use turbo_tasks::{Value, Vc};
4-
use turbo_tasks_fs::{File, FileSystemPath};
4+
use turbo_tasks_fs::FileSystemPath;
55
use turbopack_binding::turbopack::core::{
6-
asset::AssetContent, context::AssetContext, module::Module, reference_type::ReferenceType,
7-
virtual_source::VirtualSource,
6+
context::AssetContext, module::Module, reference_type::ReferenceType,
87
};
98

10-
use crate::util::{load_next_js_template, virtual_next_js_template_path};
9+
use crate::util::load_next_js_template;
1110

1211
#[turbo_tasks::function]
1312
pub async fn middleware_files(page_extensions: Vc<Vec<String>>) -> Result<Vc<Vec<String>>> {
@@ -29,23 +28,25 @@ pub async fn get_middleware_module(
2928
project_root: Vc<FileSystemPath>,
3029
userland_module: Vc<Box<dyn Module>>,
3130
) -> Result<Vc<Box<dyn Module>>> {
32-
let template_file = "middleware.js";
31+
const INNER: &str = "INNER_MIDDLEWARE_MODULE";
3332

3433
// Load the file from the next.js codebase.
35-
let file = load_next_js_template(project_root, template_file.to_string()).await?;
36-
37-
let file = File::from(file.clone_value());
38-
39-
let template_path = virtual_next_js_template_path(project_root, template_file.to_string());
40-
41-
let virtual_source = VirtualSource::new(template_path, AssetContent::file(file.into()));
34+
let source = load_next_js_template(
35+
"middleware.js",
36+
project_root,
37+
indexmap! {
38+
"VAR_USERLAND" => INNER.to_string(),
39+
},
40+
indexmap! {},
41+
)
42+
.await?;
4243

4344
let inner_assets = indexmap! {
44-
"VAR_USERLAND".to_string() => userland_module
45+
INNER.to_string() => userland_module
4546
};
4647

4748
let module = context.process(
48-
Vc::upcast(virtual_source),
49+
source,
4950
Value::new(ReferenceType::Internal(Vc::cell(inner_assets))),
5051
);
5152

packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs

Lines changed: 30 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
use std::io::Write;
22

33
use anyhow::{bail, Result};
4+
use indexmap::indexmap;
45
use turbo_tasks::{TryJoinIterExt, Value, ValueToString, Vc};
56
use turbopack_binding::{
67
turbo::tasks_fs::{rope::RopeBuilder, File, FileSystemPath},
78
turbopack::{
89
core::{
9-
asset::AssetContent, context::AssetContext, reference_type::ReferenceType,
10+
asset::{Asset, AssetContent},
11+
context::AssetContext,
12+
module::Module,
13+
reference_type::ReferenceType,
14+
source::Source,
1015
virtual_source::VirtualSource,
1116
},
1217
ecmascript::{chunk::EcmascriptChunkPlaceable, utils::StringifyJs},
@@ -22,7 +27,7 @@ use crate::{
2227
next_app::{AppPage, AppPath},
2328
next_server_component::NextServerComponentTransition,
2429
parse_segment_config_from_loader_tree,
25-
util::{load_next_js_template, virtual_next_js_template_path, NextRuntime},
30+
util::{file_content_rope, load_next_js_template, NextRuntime},
2631
};
2732

2833
/// Computes the entry for a Next.js app page.
@@ -70,59 +75,32 @@ pub async fn get_app_page_entry(
7075
let original_name = page.to_string();
7176
let pathname = AppPath::from(page.clone()).to_string();
7277

73-
let template_file = "app-page.js";
74-
7578
// Load the file from the next.js codebase.
76-
let file = load_next_js_template(project_root, template_file.to_string()).await?;
77-
78-
let mut file = file
79-
.to_str()?
80-
.replace(
81-
"\"VAR_DEFINITION_PAGE\"",
82-
&StringifyJs(&page.to_string()).to_string(),
83-
)
84-
.replace(
85-
"\"VAR_DEFINITION_PATHNAME\"",
86-
&StringifyJs(&pathname).to_string(),
87-
)
88-
.replace(
89-
"\"VAR_ORIGINAL_PATHNAME\"",
90-
&StringifyJs(&original_name).to_string(),
91-
)
92-
// TODO(alexkirsz) Support custom global error.
93-
.replace(
94-
"\"VAR_MODULE_GLOBAL_ERROR\"",
95-
&StringifyJs("next/dist/client/components/error-boundary").to_string(),
96-
)
97-
.replace(
98-
"// INJECT:tree",
99-
format!("const tree = {};", loader_tree_code).as_str(),
100-
)
101-
.replace(
102-
"// INJECT:pages",
103-
format!("const pages = {};", StringifyJs(&pages)).as_str(),
104-
)
105-
.replace(
106-
"// INJECT:__next_app_require__",
107-
"const __next_app_require__ = __turbopack_require__",
108-
)
109-
.replace(
110-
"// INJECT:__next_app_load_chunk__",
111-
"const __next_app_load_chunk__ = __turbopack_load__",
112-
);
113-
114-
// Ensure that the last line is a newline.
115-
if !file.ends_with('\n') {
116-
file.push('\n');
117-
}
118-
119-
result.concat(&file.into());
79+
let source = load_next_js_template(
80+
"app-page.js",
81+
project_root,
82+
indexmap! {
83+
"VAR_DEFINITION_PAGE" => page.to_string(),
84+
"VAR_DEFINITION_PATHNAME" => pathname.clone(),
85+
"VAR_ORIGINAL_PATHNAME" => original_name.clone(),
86+
// TODO(alexkirsz) Support custom global error.
87+
"VAR_MODULE_GLOBAL_ERROR" => "next/dist/client/components/error-boundary".to_string(),
88+
},
89+
indexmap! {
90+
"tree" => loader_tree_code,
91+
"pages" => StringifyJs(&pages).to_string(),
92+
"__next_app_require__" => "__turbopack_require__".to_string(),
93+
"__next_app_load_chunk__" => " __turbopack_load__".to_string(),
94+
},
95+
)
96+
.await?;
12097

121-
let file = File::from(result.build());
98+
let source_content = &*file_content_rope(source.content().file_content()).await?;
12299

123-
let template_path = virtual_next_js_template_path(project_root, template_file.to_string());
100+
result.concat(source_content);
124101

125-
let source = VirtualSource::new(template_path, AssetContent::file(file.into()));
102+
let file = File::from(result.build());
103+
let source = VirtualSource::new(source.ident().path(), AssetContent::file(file.into()));
126104

127105
let rsc_entry = context.process(
128106
Vc::upcast(source),
@@ -140,7 +118,7 @@ pub async fn get_app_page_entry(
140118
};
141119

142120
Ok(AppEntry {
143-
pathname: pathname.to_string(),
121+
pathname,
144122
original_name,
145123
rsc_entry,
146124
config,

packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs

Lines changed: 21 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use turbopack_binding::{
2323
use crate::{
2424
next_app::{AppEntry, AppPage, AppPath},
2525
parse_segment_config_from_source,
26-
util::{load_next_js_template, virtual_next_js_template_path, NextRuntime},
26+
util::{load_next_js_template, NextRuntime},
2727
};
2828

2929
/// Computes the entry for a Next.js app route.
@@ -49,70 +49,40 @@ pub async fn get_app_route_entry(
4949
nodejs_context
5050
};
5151

52-
let mut result = RopeBuilder::default();
53-
5452
let original_name = page.to_string();
5553
let pathname = AppPath::from(page.clone()).to_string();
5654

5755
let path = source.ident().path();
5856

59-
let template_file = "app-route.js";
57+
const INNER: &str = "INNER_APP_ROUTE";
6058

6159
// Load the file from the next.js codebase.
62-
let file = load_next_js_template(project_root, template_file.to_string()).await?;
63-
64-
let mut file = file
65-
.to_str()?
66-
.replace(
67-
"\"VAR_DEFINITION_PAGE\"",
68-
&StringifyJs(&original_name).to_string(),
69-
)
70-
.replace(
71-
"\"VAR_DEFINITION_PATHNAME\"",
72-
&StringifyJs(&pathname).to_string(),
73-
)
74-
.replace(
75-
"\"VAR_DEFINITION_FILENAME\"",
76-
&StringifyJs(&path.file_stem().await?.as_ref().unwrap().clone()).to_string(),
77-
)
78-
// TODO(alexkirsz) Is this necessary?
79-
.replace(
80-
"\"VAR_DEFINITION_BUNDLE_PATH\"",
81-
&StringifyJs("").to_string(),
82-
)
83-
.replace(
84-
"\"VAR_ORIGINAL_PATHNAME\"",
85-
&StringifyJs(&original_name).to_string(),
86-
)
87-
.replace(
88-
"\"VAR_RESOLVED_PAGE_PATH\"",
89-
&StringifyJs(&path.to_string().await?).to_string(),
90-
)
91-
.replace(
92-
"// INJECT:nextConfigOutput",
93-
"const nextConfigOutput = \"\"",
94-
);
95-
96-
// Ensure that the last line is a newline.
97-
if !file.ends_with('\n') {
98-
file.push('\n');
99-
}
100-
101-
result.concat(&file.into());
102-
103-
let file = File::from(result.build());
104-
105-
let template_path = virtual_next_js_template_path(project_root, template_file.to_string());
106-
107-
let virtual_source = VirtualSource::new(template_path, AssetContent::file(file.into()));
60+
let virtual_source = load_next_js_template(
61+
"app-route.js",
62+
project_root,
63+
indexmap! {
64+
"VAR_DEFINITION_PAGE" => page.to_string(),
65+
"VAR_DEFINITION_PATHNAME" => pathname.clone(),
66+
"VAR_DEFINITION_FILENAME" => path.file_stem().await?.as_ref().unwrap().clone(),
67+
// TODO(alexkirsz) Is this necessary?
68+
"VAR_DEFINITION_BUNDLE_PATH" => "".to_string(),
69+
"VAR_ORIGINAL_PATHNAME" => original_name.clone(),
70+
"VAR_RESOLVED_PAGE_PATH" => path.to_string().await?.clone_value(),
71+
"VAR_USERLAND" => INNER.to_string(),
72+
},
73+
indexmap! {
74+
"nextConfigOutput" => "\"\"".to_string(),
75+
},
76+
)
77+
.await?;
10878

10979
let userland_module = context.process(
11080
source,
11181
Value::new(ReferenceType::Entry(EntryReferenceSubType::AppRoute)),
11282
);
11383

11484
let inner_assets = indexmap! {
115-
"VAR_USERLAND".to_string() => userland_module
85+
INNER.to_string() => userland_module
11686
};
11787

11888
let mut rsc_entry = context.process(

0 commit comments

Comments
 (0)