Skip to content

Commit

Permalink
feat: port next.js template loading logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ForsakenHarmony committed Oct 4, 2023
1 parent ad8601d commit 19d0034
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 182 deletions.
29 changes: 15 additions & 14 deletions packages/next-swc/crates/next-core/src/middleware.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use anyhow::Result;
use indexmap::indexmap;
use turbo_tasks::{Value, Vc};
use turbo_tasks_fs::{File, FileSystemPath};
use turbo_tasks_fs::FileSystemPath;
use turbopack_binding::turbopack::core::{
asset::AssetContent, context::AssetContext, module::Module, reference_type::ReferenceType,
virtual_source::VirtualSource,
context::AssetContext, module::Module, reference_type::ReferenceType,
};

use crate::util::{load_next_js_template, virtual_next_js_template_path};
use crate::util::load_next_js_template;

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

// Load the file from the next.js codebase.
let file = load_next_js_template(project_root, template_file.to_string()).await?;

let file = File::from(file.clone_value());

let template_path = virtual_next_js_template_path(project_root, template_file.to_string());

let virtual_source = VirtualSource::new(template_path, AssetContent::file(file.into()));
let source = load_next_js_template(
"middleware.js",
project_root,
indexmap! {
"VAR_USERLAND" => INNER.to_string(),
},
indexmap! {},
)
.await?;

let inner_assets = indexmap! {
"VAR_USERLAND".to_string() => userland_module
INNER.to_string() => userland_module
};

let module = context.process(
Vc::upcast(virtual_source),
source,
Value::new(ReferenceType::Internal(Vc::cell(inner_assets))),
);

Expand Down
82 changes: 30 additions & 52 deletions packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use std::io::Write;

use anyhow::{bail, Result};
use indexmap::indexmap;
use turbo_tasks::{TryJoinIterExt, Value, ValueToString, Vc};
use turbopack_binding::{
turbo::tasks_fs::{rope::RopeBuilder, File, FileSystemPath},
turbopack::{
core::{
asset::AssetContent, context::AssetContext, reference_type::ReferenceType,
asset::{Asset, AssetContent},
context::AssetContext,
module::Module,
reference_type::ReferenceType,
source::Source,
virtual_source::VirtualSource,
},
ecmascript::{chunk::EcmascriptChunkPlaceable, utils::StringifyJs},
Expand All @@ -22,7 +27,7 @@ use crate::{
next_app::{AppPage, AppPath},
next_server_component::NextServerComponentTransition,
parse_segment_config_from_loader_tree,
util::{load_next_js_template, virtual_next_js_template_path, NextRuntime},
util::{file_content_rope, load_next_js_template, NextRuntime},
};

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

let template_file = "app-page.js";

// Load the file from the next.js codebase.
let file = load_next_js_template(project_root, template_file.to_string()).await?;

let mut file = file
.to_str()?
.replace(
"\"VAR_DEFINITION_PAGE\"",
&StringifyJs(&page.to_string()).to_string(),
)
.replace(
"\"VAR_DEFINITION_PATHNAME\"",
&StringifyJs(&pathname).to_string(),
)
.replace(
"\"VAR_ORIGINAL_PATHNAME\"",
&StringifyJs(&original_name).to_string(),
)
// TODO(alexkirsz) Support custom global error.
.replace(
"\"VAR_MODULE_GLOBAL_ERROR\"",
&StringifyJs("next/dist/client/components/error-boundary").to_string(),
)
.replace(
"// INJECT:tree",
format!("const tree = {};", loader_tree_code).as_str(),
)
.replace(
"// INJECT:pages",
format!("const pages = {};", StringifyJs(&pages)).as_str(),
)
.replace(
"// INJECT:__next_app_require__",
"const __next_app_require__ = __turbopack_require__",
)
.replace(
"// INJECT:__next_app_load_chunk__",
"const __next_app_load_chunk__ = __turbopack_load__",
);

// Ensure that the last line is a newline.
if !file.ends_with('\n') {
file.push('\n');
}

result.concat(&file.into());
let source = load_next_js_template(
"app-page.js",
project_root,
indexmap! {
"VAR_DEFINITION_PAGE" => page.to_string(),
"VAR_DEFINITION_PATHNAME" => pathname.clone(),
"VAR_ORIGINAL_PATHNAME" => original_name.clone(),
// TODO(alexkirsz) Support custom global error.
"VAR_MODULE_GLOBAL_ERROR" => "next/dist/client/components/error-boundary".to_string(),
},
indexmap! {
"tree" => loader_tree_code,
"pages" => StringifyJs(&pages).to_string(),
"__next_app_require__" => "__turbopack_require__".to_string(),
"__next_app_load_chunk__" => " __turbopack_load__".to_string(),
},
)
.await?;

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

let template_path = virtual_next_js_template_path(project_root, template_file.to_string());
result.concat(source_content);

let source = VirtualSource::new(template_path, AssetContent::file(file.into()));
let file = File::from(result.build());
let source = VirtualSource::new(source.ident().path(), AssetContent::file(file.into()));

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

Ok(AppEntry {
pathname: pathname.to_string(),
pathname,
original_name,
rsc_entry,
config,
Expand Down
72 changes: 21 additions & 51 deletions packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use turbopack_binding::{
use crate::{
next_app::{AppEntry, AppPage, AppPath},
parse_segment_config_from_source,
util::{load_next_js_template, virtual_next_js_template_path, NextRuntime},
util::{load_next_js_template, NextRuntime},
};

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

let mut result = RopeBuilder::default();

let original_name = page.to_string();
let pathname = AppPath::from(page.clone()).to_string();

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

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

// Load the file from the next.js codebase.
let file = load_next_js_template(project_root, template_file.to_string()).await?;

let mut file = file
.to_str()?
.replace(
"\"VAR_DEFINITION_PAGE\"",
&StringifyJs(&original_name).to_string(),
)
.replace(
"\"VAR_DEFINITION_PATHNAME\"",
&StringifyJs(&pathname).to_string(),
)
.replace(
"\"VAR_DEFINITION_FILENAME\"",
&StringifyJs(&path.file_stem().await?.as_ref().unwrap().clone()).to_string(),
)
// TODO(alexkirsz) Is this necessary?
.replace(
"\"VAR_DEFINITION_BUNDLE_PATH\"",
&StringifyJs("").to_string(),
)
.replace(
"\"VAR_ORIGINAL_PATHNAME\"",
&StringifyJs(&original_name).to_string(),
)
.replace(
"\"VAR_RESOLVED_PAGE_PATH\"",
&StringifyJs(&path.to_string().await?).to_string(),
)
.replace(
"// INJECT:nextConfigOutput",
"const nextConfigOutput = \"\"",
);

// Ensure that the last line is a newline.
if !file.ends_with('\n') {
file.push('\n');
}

result.concat(&file.into());

let file = File::from(result.build());

let template_path = virtual_next_js_template_path(project_root, template_file.to_string());

let virtual_source = VirtualSource::new(template_path, AssetContent::file(file.into()));
let virtual_source = load_next_js_template(
"app-route.js",
project_root,
indexmap! {
"VAR_DEFINITION_PAGE" => page.to_string(),
"VAR_DEFINITION_PATHNAME" => pathname.clone(),
"VAR_DEFINITION_FILENAME" => path.file_stem().await?.as_ref().unwrap().clone(),
// TODO(alexkirsz) Is this necessary?
"VAR_DEFINITION_BUNDLE_PATH" => "".to_string(),
"VAR_ORIGINAL_PATHNAME" => original_name.clone(),
"VAR_RESOLVED_PAGE_PATH" => path.to_string().await?.clone_value(),
"VAR_USERLAND" => INNER.to_string(),
},
indexmap! {
"nextConfigOutput" => "\"\"".to_string(),
},
)
.await?;

let userland_module = context.process(
source,
Value::new(ReferenceType::Entry(EntryReferenceSubType::AppRoute)),
);

let inner_assets = indexmap! {
"VAR_USERLAND".to_string() => userland_module
INNER.to_string() => userland_module
};

let mut rsc_entry = context.process(
Expand Down
Loading

0 comments on commit 19d0034

Please sign in to comment.