Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge EcmascriptChunkUpdates before sending them to the client
Browse files Browse the repository at this point in the history
alexkirsz committed Feb 27, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 9ec7680 commit 5a5db52
Showing 48 changed files with 2,000 additions and 351 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
@@ -18,7 +18,7 @@ subscribeToUpdate(
},
},
(update) => {
if (update.type === "restart") {
if (update.type === "restart" || update.type === "notFound") {
location.reload();
}
}
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
@@ -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;
}

2 changes: 1 addition & 1 deletion crates/next-core/src/fallback.rs
Original file line number Diff line number Diff line change
@@ -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"),
1 change: 1 addition & 0 deletions crates/next-core/src/next_client/transition.rs
Original file line number Diff line number Diff line change
@@ -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),
};

1 change: 1 addition & 0 deletions crates/next-core/src/next_edge/transition.rs
Original file line number Diff line number Diff line change
@@ -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,
};

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},
@@ -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())
}
@@ -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)))
7 changes: 5 additions & 2 deletions crates/next-core/src/web_entry_source.rs
Original file line number Diff line number Diff line change
@@ -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
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 5a5db52

Please sign in to comment.