Skip to content

Commit

Permalink
Merge EcmascriptChunkUpdates before sending them to the client
Browse files Browse the repository at this point in the history
  • Loading branch information
alexkirsz committed Feb 27, 2023
1 parent 041f42e commit 670aa30
Show file tree
Hide file tree
Showing 47 changed files with 1,982 additions and 313 deletions.
405 changes: 292 additions & 113 deletions crates/next-core/js/src/dev/hmr-client.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion crates/next-core/js/src/entry/fallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ subscribeToUpdate(
},
},
(update) => {
if (update.type === "restart") {
if (update.type === "restart" || update.type === "notFound") {
location.reload();
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/next-core/js/src/entry/next-hydrate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ function subscribeToPageManifest({ assetPrefix }: { assetPrefix: string }) {
path: "_next/static/development/_devPagesManifest.json",
},
(update) => {
if (["restart", "partial"].includes(update.type)) {
if (["restart", "notFound", "partial"].includes(update.type)) {
return;
}

Expand Down
2 changes: 1 addition & 1 deletion crates/next-core/src/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ pub async fn get_fallback_page(
bail!("fallback runtime entry is not an ecmascript module");
};

let chunk = module.as_evaluated_chunk(chunking_context, Some(runtime_entries));
let chunk = module.as_evaluated_chunk(chunking_context, Some(runtime_entries), None);

Ok(DevHtmlAssetVc::new(
dev_server_root.join("fallback.html"),
Expand Down
1 change: 1 addition & 0 deletions crates/next-core/src/next_client/transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ impl Transition for NextClientTransition {
asset: chunkable_asset,
chunking_context: self.client_chunking_context,
base_path: self.server_root.join("_next"),
server_root: self.server_root,
runtime_entries: Some(runtime_entries),
};

Expand Down
1 change: 1 addition & 0 deletions crates/next-core/src/next_edge/transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ impl Transition for NextEdgeTransition {
asset: new_asset.into(),
chunking_context: self.edge_chunking_context,
base_path: self.output_path,
server_root: self.output_path,
runtime_entries: None,
};

Expand Down
35 changes: 24 additions & 11 deletions crates/next-core/src/page_loader.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::io::Write;

use anyhow::{bail, Result};
use serde_json::Value;
use turbo_tasks::primitives::StringVc;
use turbo_tasks::{primitives::StringVc, TryJoinIterExt};
use turbo_tasks_fs::{rope::RopeBuilder, File, FileContent, FileSystemPathVc};
use turbopack_core::{
asset::{Asset, AssetContentVc, AssetVc},
Expand Down Expand Up @@ -87,8 +86,11 @@ impl PageLoaderAssetVc {
this.client_context.compile_time_info(),
);

let chunk_group =
ChunkGroupVc::from_chunk(asset.as_evaluated_chunk(this.client_chunking_context, None));
let chunk_group = ChunkGroupVc::from_chunk(asset.as_evaluated_chunk(
this.client_chunking_context,
None,
None,
));

Ok(chunk_group.chunks())
}
Expand All @@ -109,17 +111,28 @@ impl Asset for PageLoaderAsset {
let this = &*self_vc.await?;

let chunks = self_vc.get_page_chunks().await?;

let mut data = Vec::with_capacity(chunks.len());
for chunk in chunks.iter() {
let path = chunk.path().await?;
data.push(Value::String(path.path.clone()));
}
let server_root = this.server_root.await?;

let chunk_paths: Vec<_> = chunks
.iter()
.map(|chunk| {
let server_root = server_root.clone();
async move {
Ok(server_root
.get_path_to(&*chunk.path().await?)
.map(|path| path.to_string()))
}
})
.try_join()
.await?
.into_iter()
.flatten()
.collect();

let content = format!(
"__turbopack_load_page_chunks__({}, {})\n",
stringify_js(&this.pathname.await?),
Value::Array(data)
stringify_js(&chunk_paths)
);

Ok(AssetContentVc::from(File::from(content)))
Expand Down
7 changes: 5 additions & 2 deletions crates/next-core/src/web_entry_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,11 @@ pub async fn create_web_entry_source(
.enumerate()
.map(|(i, module)| async move {
if let Some(ecmascript) = EcmascriptModuleAssetVc::resolve_from(module).await? {
Ok(ecmascript
.as_evaluated_chunk(chunking_context, (i == 0).then_some(runtime_entries)))
Ok(ecmascript.as_evaluated_chunk(
chunking_context,
(i == 0).then_some(runtime_entries),
None,
))
} else if let Some(chunkable) = ChunkableAssetVc::resolve_from(module).await? {
// TODO this is missing runtime code, so it's probably broken and we should also
// add an ecmascript chunk with the runtime code
Expand Down
82 changes: 82 additions & 0 deletions crates/turbopack-core/src/chunk/list/asset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use anyhow::Result;
use turbo_tasks_fs::FileSystemPathVc;

use super::content::ChunkListContentVc;
use crate::{
asset::{Asset, AssetContentVc, AssetVc},
chunk::{ChunkGroupVc, ChunkReferenceVc, ChunksVc},
reference::AssetReferencesVc,
version::{VersionedContent, VersionedContentVc},
};

#[turbo_tasks::value(shared)]
pub(super) struct ChunkListAsset {
server_root: FileSystemPathVc,
chunk_group: ChunkGroupVc,
path: FileSystemPathVc,
}

#[turbo_tasks::value_impl]
impl ChunkListAssetVc {
#[turbo_tasks::function]
pub fn new(
server_root: FileSystemPathVc,
chunk_group: ChunkGroupVc,
path: FileSystemPathVc,
) -> Self {
ChunkListAsset {
server_root,
chunk_group,
path,
}
.cell()
}

#[turbo_tasks::function]
async fn get_chunks(self) -> Result<ChunksVc> {
Ok(self.await?.chunk_group.chunks())
}

#[turbo_tasks::function]
async fn content(self) -> Result<ChunkListContentVc> {
let this = &*self.await?;
Ok(ChunkListContentVc::new(
this.server_root,
this.chunk_group.chunks(),
))
}
}

#[turbo_tasks::value_impl]
impl Asset for ChunkListAsset {
#[turbo_tasks::function]
fn path(&self) -> FileSystemPathVc {
self.path
}

#[turbo_tasks::function]
async fn references(&self) -> Result<AssetReferencesVc> {
let chunks = self.chunk_group.chunks().await?;

let mut references = Vec::with_capacity(chunks.len());
for chunk in chunks.iter() {
references.push(ChunkReferenceVc::new(*chunk).into());

// We also need to expand references one step here, because the chunk asset
// graph is lazy. TODO(alexkirsz) Better explaination.
references.extend(chunk.references().await?.iter().copied());
}

Ok(AssetReferencesVc::cell(references))
}

#[turbo_tasks::function]
fn content(self_vc: ChunkListAssetVc) -> AssetContentVc {
self_vc.content().content()
}

#[turbo_tasks::function]
fn versioned_content(self_vc: ChunkListAssetVc) -> VersionedContentVc {
self_vc.content().into()
}
}
119 changes: 119 additions & 0 deletions crates/turbopack-core/src/chunk/list/content.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use anyhow::Result;
use indexmap::IndexMap;
use turbo_tasks::{IntoTraitRef, TraitRef, TryJoinIterExt};
use turbo_tasks_fs::{FileContent, FileSystemPathReadRef, FileSystemPathVc};

use super::{
update::update_chunk_list,
version::{ChunkListVersion, ChunkListVersionVc},
};
use crate::{
asset::{Asset, AssetContent, AssetContentVc},
chunk::ChunksVc,
version::{
MergeableVersionedContent, MergeableVersionedContentVc, UpdateVc, VersionVc,
VersionedContent, VersionedContentMerger, VersionedContentTraitRef, VersionedContentVc,
VersionedContentsVc,
},
};

#[turbo_tasks::value(serialization = "none")]
pub(super) struct ChunkListContent {
pub server_root: FileSystemPathReadRef,
#[turbo_tasks(trace_ignore)]
pub chunks_contents: IndexMap<String, VersionedContentTraitRef>,
}

#[turbo_tasks::value_impl]
impl ChunkListContentVc {
#[turbo_tasks::function]
pub async fn new(server_root: FileSystemPathVc, chunks: ChunksVc) -> Result<Self> {
let server_root = server_root.await?;
Ok(ChunkListContent {
server_root: server_root.clone(),
chunks_contents: chunks
.await?
.iter()
.map(|chunk| {
let server_root = server_root.clone();
async move {
Ok((
server_root
.get_path_to(&*chunk.path().await?)
.map(|path| path.to_string()),
chunk.versioned_content().into_trait_ref().await?,
))
}
})
.try_join()
.await?
.into_iter()
.filter_map(|(path, content)| path.map(|path| (path, content)))
.collect(),
}
.cell())
}

#[turbo_tasks::function]
pub async fn version(self) -> Result<ChunkListVersionVc> {
let this = self.await?;

let mut by_merger = IndexMap::<_, Vec<_>>::new();
let mut by_path = IndexMap::<_, _>::new();

for (chunk_path, chunk_content) in &this.chunks_contents {
let chunk_content = TraitRef::cell(chunk_content.clone());
if let Some(mergeable) =
MergeableVersionedContentVc::resolve_from(chunk_content).await?
{
let merger = mergeable.get_merger().resolve().await?;
by_merger.entry(merger).or_default().push(chunk_content);
} else {
by_path.insert(
chunk_path.clone(),
chunk_content.version().into_trait_ref().await?,
);
}
}

let by_merger = by_merger
.into_iter()
.map(|(merger, contents)| {
let merger = merger.clone();
async move {
Ok((
merger,
merger
.merge(VersionedContentsVc::cell(contents))
.version()
.into_trait_ref()
.await?,
))
}
})
.try_join()
.await?
.into_iter()
.collect();

Ok(ChunkListVersion { by_path, by_merger }.cell())
}
}

#[turbo_tasks::value_impl]
impl VersionedContent for ChunkListContent {
#[turbo_tasks::function]
fn content(&self) -> AssetContentVc {
AssetContentVc::cell(AssetContent::File(FileContent::NotFound.into()))
}

#[turbo_tasks::function]
fn version(self_vc: ChunkListContentVc) -> VersionVc {
self_vc.version().into()
}

#[turbo_tasks::function]
fn update(self_vc: ChunkListContentVc, from_version: VersionVc) -> UpdateVc {
update_chunk_list(self_vc, from_version)
}
}
5 changes: 5 additions & 0 deletions crates/turbopack-core/src/chunk/list/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub(crate) mod asset;
pub(crate) mod content;
pub(crate) mod reference;
pub(crate) mod update;
pub(crate) mod version;
67 changes: 67 additions & 0 deletions crates/turbopack-core/src/chunk/list/reference.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use anyhow::Result;
use turbo_tasks::{primitives::StringVc, ValueToString, ValueToStringVc};
use turbo_tasks_fs::FileSystemPathVc;

use super::asset::ChunkListAssetVc;
use crate::{
chunk::{
ChunkGroupVc, ChunkableAssetReference, ChunkableAssetReferenceVc, ChunkingContextVc,
ChunkingType, ChunkingTypeOptionVc,
},
reference::{AssetReference, AssetReferenceVc},
resolve::{ResolveResult, ResolveResultVc},
};

#[turbo_tasks::value]
pub struct ChunkListReference {
server_root: FileSystemPathVc,
chunk_group: ChunkGroupVc,
path: FileSystemPathVc,
}

#[turbo_tasks::value_impl]
impl ChunkListReferenceVc {
#[turbo_tasks::function]
pub fn new(
server_root: FileSystemPathVc,
chunk_group: ChunkGroupVc,
path: FileSystemPathVc,
) -> Self {
ChunkListReference {
server_root,
chunk_group,
path,
}
.cell()
}
}

#[turbo_tasks::value_impl]
impl ValueToString for ChunkListReference {
#[turbo_tasks::function]
async fn to_string(&self) -> Result<StringVc> {
Ok(StringVc::cell(format!(
"referenced chunk list {}",
self.path.to_string().await?
)))
}
}

#[turbo_tasks::value_impl]
impl AssetReference for ChunkListReference {
#[turbo_tasks::function]
fn resolve_reference(&self) -> ResolveResultVc {
ResolveResult::asset(
ChunkListAssetVc::new(self.server_root, self.chunk_group, self.path).into(),
)
.cell()
}
}

#[turbo_tasks::value_impl]
impl ChunkableAssetReference for ChunkListReference {
#[turbo_tasks::function]
fn chunking_type(&self, _context: ChunkingContextVc) -> ChunkingTypeOptionVc {
ChunkingTypeOptionVc::cell(Some(ChunkingType::Separate))
}
}
Loading

0 comments on commit 670aa30

Please sign in to comment.