From dd56a77e9163a5cb334d2765407e3835cf0c3a48 Mon Sep 17 00:00:00 2001 From: Alex Kirszenberg Date: Mon, 17 Jul 2023 18:03:14 +0200 Subject: [PATCH] Turbopack: Experimental dev app pages support (#52680) This implements app pages and routes for the Nexturbo API. ## Turbopack updates * https://github.com/vercel/turbo/pull/5527 --- Cargo.lock | 75 ++- Cargo.toml | 6 +- packages/next-swc/crates/next-api/Cargo.toml | 1 + packages/next-swc/crates/next-api/src/app.rs | 625 +++++++++++++++++- .../crates/next-api/src/entrypoints.rs | 9 + packages/next-swc/crates/next-api/src/lib.rs | 1 + .../next-swc/crates/next-api/src/pages.rs | 506 +++++++++++--- .../next-swc/crates/next-api/src/project.rs | 294 ++------ .../next-swc/crates/next-build/src/lib.rs | 1 - .../next-build/src/next_app/app_entries.rs | 349 ++-------- .../crates/next-build/src/next_app/mod.rs | 4 - .../crates/next-build/src/next_build.rs | 58 +- .../next-build/src/next_pages/page_entries.rs | 3 +- packages/next-swc/crates/next-core/Cargo.toml | 2 + .../next-swc/crates/next-core/js/package.json | 4 +- .../crates/next-core/src/app_source.rs | 2 +- .../crates/next-core/src/app_structure.rs | 2 +- .../src/{manifest.rs => dev_manifest.rs} | 0 .../next-swc/crates/next-core/src/emit.rs | 3 + packages/next-swc/crates/next-core/src/lib.rs | 6 +- .../next_app/app_client_references_chunks.rs} | 64 +- .../src/next_app/app_client_shared_chunks.rs | 55 ++ .../next-core/src/next_app/app_entry.rs | 14 + .../src/next_app/app_favicon_entry.rs} | 14 +- .../src/next_app/app_page_entry.rs | 23 +- .../src/next_app/app_route_entry.rs | 9 +- .../crates/next-core/src/next_app/mod.rs | 16 + .../src/next_client_reference/mod.rs | 5 +- .../visit_client_reference.rs | 89 ++- .../client_reference_manifest.rs | 217 ++++++ .../src/next_manifests/mod.rs} | 5 +- packages/next-swc/crates/next-dev/src/lib.rs | 2 +- packages/next/src/cli/next-dev.ts | 6 + pnpm-lock.yaml | 18 +- 34 files changed, 1627 insertions(+), 861 deletions(-) create mode 100644 packages/next-swc/crates/next-api/src/entrypoints.rs rename packages/next-swc/crates/next-core/src/{manifest.rs => dev_manifest.rs} (100%) rename packages/next-swc/crates/{next-build/src/next_app/app_client_reference.rs => next-core/src/next_app/app_client_references_chunks.rs} (66%) create mode 100644 packages/next-swc/crates/next-core/src/next_app/app_client_shared_chunks.rs create mode 100644 packages/next-swc/crates/next-core/src/next_app/app_entry.rs rename packages/next-swc/crates/{next-build/src/next_app/app_route_favicon_entry.rs => next-core/src/next_app/app_favicon_entry.rs} (91%) rename packages/next-swc/crates/{next-build => next-core}/src/next_app/app_page_entry.rs (93%) rename packages/next-swc/crates/{next-build => next-core}/src/next_app/app_route_entry.rs (96%) create mode 100644 packages/next-swc/crates/next-core/src/next_manifests/client_reference_manifest.rs rename packages/next-swc/crates/{next-build/src/manifests.rs => next-core/src/next_manifests/mod.rs} (98%) diff --git a/Cargo.lock b/Cargo.lock index 1de0fb802f3be..e32a9b854c39c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -412,7 +412,7 @@ dependencies = [ [[package]] name = "auto-hash-map" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "serde", ] @@ -3377,6 +3377,7 @@ dependencies = [ "next-core", "once_cell", "serde", + "serde_json", "tokio", "tracing", "tracing-subscriber", @@ -3416,12 +3417,14 @@ dependencies = [ "anyhow", "async-recursion", "async-trait", + "base64 0.21.0", "const_format", "futures", "indexmap", "indoc", "lazy_static", "mime", + "mime_guess", "next-transform-dynamic", "next-transform-font", "next-transform-strip-page-exports", @@ -3599,7 +3602,7 @@ dependencies = [ [[package]] name = "node-file-trace" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "serde", @@ -7259,7 +7262,7 @@ dependencies = [ [[package]] name = "turbo-tasks" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "async-trait", @@ -7291,7 +7294,7 @@ dependencies = [ [[package]] name = "turbo-tasks-build" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "cargo-lock", @@ -7303,7 +7306,7 @@ dependencies = [ [[package]] name = "turbo-tasks-bytes" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "bytes", @@ -7318,7 +7321,7 @@ dependencies = [ [[package]] name = "turbo-tasks-env" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "dotenvs", @@ -7332,7 +7335,7 @@ dependencies = [ [[package]] name = "turbo-tasks-fetch" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "indexmap", @@ -7349,7 +7352,7 @@ dependencies = [ [[package]] name = "turbo-tasks-fs" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "auto-hash-map", @@ -7379,7 +7382,7 @@ dependencies = [ [[package]] name = "turbo-tasks-hash" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "base16", "hex", @@ -7391,7 +7394,7 @@ dependencies = [ [[package]] name = "turbo-tasks-macros" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -7405,7 +7408,7 @@ dependencies = [ [[package]] name = "turbo-tasks-macros-shared" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "proc-macro2", "quote", @@ -7415,7 +7418,7 @@ dependencies = [ [[package]] name = "turbo-tasks-malloc" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "mimalloc", ] @@ -7423,7 +7426,7 @@ dependencies = [ [[package]] name = "turbo-tasks-memory" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "auto-hash-map", @@ -7446,7 +7449,7 @@ dependencies = [ [[package]] name = "turbo-tasks-testing" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "auto-hash-map", @@ -7459,7 +7462,7 @@ dependencies = [ [[package]] name = "turbopack" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "async-recursion", @@ -7489,7 +7492,7 @@ dependencies = [ [[package]] name = "turbopack-bench" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "chromiumoxide", @@ -7519,7 +7522,7 @@ dependencies = [ [[package]] name = "turbopack-binding" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "auto-hash-map", "mdxjs", @@ -7561,7 +7564,7 @@ dependencies = [ [[package]] name = "turbopack-build" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "indexmap", @@ -7581,7 +7584,7 @@ dependencies = [ [[package]] name = "turbopack-cli-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "clap 4.1.11", @@ -7605,7 +7608,7 @@ dependencies = [ [[package]] name = "turbopack-core" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "async-trait", @@ -7633,7 +7636,7 @@ dependencies = [ [[package]] name = "turbopack-create-test-app" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "clap 4.1.11", @@ -7646,7 +7649,7 @@ dependencies = [ [[package]] name = "turbopack-css" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "async-trait", @@ -7668,7 +7671,7 @@ dependencies = [ [[package]] name = "turbopack-dev" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "indexmap", @@ -7692,7 +7695,7 @@ dependencies = [ [[package]] name = "turbopack-dev-server" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "async-compression", @@ -7728,7 +7731,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "async-trait", @@ -7761,7 +7764,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript-plugins" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "async-trait", @@ -7784,7 +7787,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript-runtime" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "indoc", @@ -7801,7 +7804,7 @@ dependencies = [ [[package]] name = "turbopack-env" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "indexmap", @@ -7817,7 +7820,7 @@ dependencies = [ [[package]] name = "turbopack-image" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "base64 0.21.0", @@ -7837,7 +7840,7 @@ dependencies = [ [[package]] name = "turbopack-json" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "serde", @@ -7852,7 +7855,7 @@ dependencies = [ [[package]] name = "turbopack-mdx" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "mdxjs", @@ -7867,7 +7870,7 @@ dependencies = [ [[package]] name = "turbopack-node" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "async-stream", @@ -7902,7 +7905,7 @@ dependencies = [ [[package]] name = "turbopack-static" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "serde", @@ -7918,7 +7921,7 @@ dependencies = [ [[package]] name = "turbopack-swc-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "swc_core", "turbo-tasks", @@ -7929,7 +7932,7 @@ dependencies = [ [[package]] name = "turbopack-test-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230716.2#8433a321545de30374b5374320625b92c663f5dc" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230717.2#b0695455647275362c780c85f7e0bcbb3d8d64ec" dependencies = [ "anyhow", "once_cell", @@ -7949,7 +7952,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if 1.0.0", - "rand 0.8.5", + "rand 0.4.6", "static_assertions", ] diff --git a/Cargo.toml b/Cargo.toml index c1bc91600e891..9625d55cf0ec2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,11 +44,11 @@ swc_core = { version = "0.79.13" } testing = { version = "0.33.20" } # Turbo crates -turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230716.2" } +turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230717.2" } # [TODO]: need to refactor embed_directory! macro usages, as well as resolving turbo_tasks::function, macros.. -turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230716.2" } +turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230717.2" } # [TODO]: need to refactor embed_directory! macro usage in next-core -turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230716.2" } +turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230717.2" } # General Deps diff --git a/packages/next-swc/crates/next-api/Cargo.toml b/packages/next-swc/crates/next-api/Cargo.toml index f3382ab2a7d4b..70b8706cc0bc9 100644 --- a/packages/next-swc/crates/next-api/Cargo.toml +++ b/packages/next-swc/crates/next-api/Cargo.toml @@ -22,6 +22,7 @@ indexmap = { workspace = true } next-core = { workspace = true } once_cell = { workspace = true } serde = { workspace = true } +serde_json = { workspace = true } tokio = { workspace = true, features = ["full"] } turbopack-binding = { workspace = true, features = [ "__turbo_tasks_memory", diff --git a/packages/next-swc/crates/next-api/src/app.rs b/packages/next-swc/crates/next-api/src/app.rs index d53a1320d0503..61254a1957377 100644 --- a/packages/next-swc/crates/next-api/src/app.rs +++ b/packages/next-swc/crates/next-api/src/app.rs @@ -1,28 +1,349 @@ -use next_core::app_structure::Entrypoint; +use anyhow::{Context, Result}; +use next_core::{ + app_structure::{ + get_entrypoints, Entrypoint as AppEntrypoint, Entrypoints as AppEntrypoints, LoaderTree, + }, + emit_all_assets, + mode::NextMode, + next_app::{ + get_app_client_references_chunks, get_app_client_shared_chunks, get_app_page_entry, + get_app_route_entry, AppEntry, + }, + next_client::{ + get_client_module_options_context, get_client_resolve_options_context, + get_client_runtime_entries, ClientContextType, + }, + next_client_reference::{ + ClientReferenceGraph, ClientReferenceType, NextEcmascriptClientReferenceTransition, + }, + next_dynamic::{NextDynamicEntries, NextDynamicTransition}, + next_manifests::{ + AppBuildManifest, AppPathsManifest, BuildManifest, ClientReferenceManifest, PagesManifest, + }, + next_server::{ + get_server_module_options_context, get_server_resolve_options_context, + get_server_runtime_entries, ServerContextType, + }, +}; use serde::{Deserialize, Serialize}; -use turbo_tasks::{trace::TraceRawVcs, Completion, Vc}; +use turbo_tasks::{trace::TraceRawVcs, Completion, TryJoinIterExt, Value, Vc}; +use turbopack_binding::{ + turbo::{ + tasks_env::{CustomProcessEnv, ProcessEnv}, + tasks_fs::{File, FileSystemPath}, + }, + turbopack::{ + core::{ + asset::{Asset, AssetContent}, + changed::any_content_changed_of_output_assets, + chunk::EvaluatableAssets, + file_source::FileSource, + output::{OutputAsset, OutputAssets}, + raw_output::RawOutput, + virtual_source::VirtualSource, + }, + turbopack::{ + module_options::ModuleOptionsContext, resolve_options_context::ResolveOptionsContext, + transition::ContextTransition, ModuleAssetContext, + }, + }, +}; + +use crate::{ + project::Project, + route::{Endpoint, Route, Routes, WrittenEndpoint}, +}; + +#[turbo_tasks::value] +pub struct AppProject { + project: Vc, + app_dir: Vc, + mode: NextMode, +} + +#[turbo_tasks::value(transparent)] +pub struct OptionAppProject(Option>); + +impl AppProject { + fn client_ty(self: Vc) -> ClientContextType { + ClientContextType::App { + app_dir: self.app_dir(), + } + } + + fn rsc_ty(self: Vc) -> ServerContextType { + ServerContextType::AppRSC { + app_dir: self.app_dir(), + client_transition: Some(Vc::upcast(self.client_transition())), + ecmascript_client_reference_transition_name: Some(self.client_transition_name()), + } + } + + fn ssr_ty(self: Vc) -> ServerContextType { + ServerContextType::AppSSR { + app_dir: self.app_dir(), + } + } +} + +const ECMASCRIPT_CLIENT_TRANSITION_NAME: &str = "next-ecmascript-client-reference"; + +#[turbo_tasks::value_impl] +impl AppProject { + #[turbo_tasks::function] + pub fn new(project: Vc, app_dir: Vc, mode: NextMode) -> Vc { + AppProject { + project, + app_dir, + mode, + } + .cell() + } + + #[turbo_tasks::function] + fn project(&self) -> Vc { + self.project + } + + #[turbo_tasks::function] + fn app_dir(&self) -> Vc { + self.app_dir + } + + #[turbo_tasks::function] + fn app_entrypoints(&self) -> Vc { + get_entrypoints(self.app_dir, self.project.next_config().page_extensions()) + } + + #[turbo_tasks::function] + async fn client_module_options_context(self: Vc) -> Result> { + let this = self.await?; + Ok(get_client_module_options_context( + self.project().project_path(), + self.project().execution_context(), + self.project().client_compile_time_info().environment(), + Value::new(self.client_ty()), + this.mode, + self.project().next_config(), + )) + } + + #[turbo_tasks::function] + async fn client_resolve_options_context(self: Vc) -> Result> { + let this = self.await?; + Ok(get_client_resolve_options_context( + self.project().project_path(), + Value::new(self.client_ty()), + this.mode, + self.project().next_config(), + self.project().execution_context(), + )) + } + + #[turbo_tasks::function] + fn client_transition_name(self: Vc) -> Vc { + Vc::cell(ECMASCRIPT_CLIENT_TRANSITION_NAME.to_string()) + } + + #[turbo_tasks::function] + fn client_transition(self: Vc) -> Vc { + ContextTransition::new( + self.project().client_compile_time_info(), + self.client_module_options_context(), + self.client_resolve_options_context(), + ) + } + + #[turbo_tasks::function] + async fn rsc_module_options_context(self: Vc) -> Result> { + let this = self.await?; + Ok(get_server_module_options_context( + self.project().project_path(), + self.project().execution_context(), + Value::new(self.rsc_ty()), + this.mode, + self.project().next_config(), + )) + } + + #[turbo_tasks::function] + async fn rsc_resolve_options_context(self: Vc) -> Result> { + let this = self.await?; + Ok(get_server_resolve_options_context( + self.project().project_path(), + Value::new(self.rsc_ty()), + this.mode, + self.project().next_config(), + self.project().execution_context(), + )) + } + + #[turbo_tasks::function] + fn rsc_module_context(self: Vc) -> Vc { + let transitions = [ + ( + ECMASCRIPT_CLIENT_TRANSITION_NAME.to_string(), + Vc::upcast(NextEcmascriptClientReferenceTransition::new( + self.client_transition(), + self.ssr_transition(), + )), + ), + ( + "next-dynamic".to_string(), + Vc::upcast(NextDynamicTransition::new(self.client_transition())), + ), + ] + .into_iter() + .collect(); + ModuleAssetContext::new( + Vc::cell(transitions), + self.project().server_compile_time_info(), + self.rsc_module_options_context(), + self.rsc_resolve_options_context(), + ) + } + + #[turbo_tasks::function] + fn client_module_context(self: Vc) -> Vc { + ModuleAssetContext::new( + Vc::cell(Default::default()), + self.project().client_compile_time_info(), + self.client_module_options_context(), + self.client_resolve_options_context(), + ) + } + + #[turbo_tasks::function] + async fn ssr_module_options_context(self: Vc) -> Result> { + let this = self.await?; + Ok(get_server_module_options_context( + self.project().project_path(), + self.project().execution_context(), + Value::new(self.ssr_ty()), + this.mode, + self.project().next_config(), + )) + } + + #[turbo_tasks::function] + async fn ssr_resolve_options_context(self: Vc) -> Result> { + let this = self.await?; + Ok(get_server_resolve_options_context( + self.project().project_path(), + Value::new(self.ssr_ty()), + this.mode, + self.project().next_config(), + self.project().execution_context(), + )) + } + + #[turbo_tasks::function] + fn ssr_transition(self: Vc) -> Vc { + ContextTransition::new( + self.project().server_compile_time_info(), + self.ssr_module_options_context(), + self.ssr_resolve_options_context(), + ) + } + + #[turbo_tasks::function] + async fn rsc_runtime_entries(self: Vc) -> Result> { + let this = self.await?; + Ok(get_server_runtime_entries( + self.project().project_path(), + // TODO(alexkirsz) Should we pass env here or EnvMap::empty, as is done in + // app_source? + self.project().env(), + Value::new(self.rsc_ty()), + this.mode, + self.project().next_config(), + ) + .resolve_entries(Vc::upcast(self.rsc_module_context()))) + } + + #[turbo_tasks::function] + fn client_env(self: Vc) -> Vc> { + Vc::upcast(CustomProcessEnv::new( + self.project().env(), + self.project().next_config().env(), + )) + } + + #[turbo_tasks::function] + async fn client_runtime_entries(self: Vc) -> Result> { + let this = self.await?; + Ok(get_client_runtime_entries( + self.project().project_path(), + self.client_env(), + Value::new(self.client_ty()), + this.mode, + self.project().next_config(), + self.project().execution_context(), + ) + .resolve_entries(Vc::upcast(self.client_module_context()))) + } -use crate::route::{Endpoint, Route, WrittenEndpoint}; + #[turbo_tasks::function] + pub async fn routes(self: Vc) -> Result> { + let app_entrypoints = self.app_entrypoints(); + Ok(Vc::cell( + app_entrypoints + .await? + .iter() + .map(|(pathname, app_entrypoint)| async { + Ok(( + pathname.clone(), + *app_entry_point_to_route(self, *app_entrypoint, pathname.clone()).await?, + )) + }) + .try_join() + .await? + .into_iter() + .collect(), + )) + } +} #[turbo_tasks::function] -pub async fn app_entry_point_to_route(entrypoint: Entrypoint) -> Vc { +pub async fn app_entry_point_to_route( + app_project: Vc, + entrypoint: AppEntrypoint, + pathname: String, +) -> Vc { match entrypoint { - Entrypoint::AppPage { .. } => Route::AppPage { + AppEntrypoint::AppPage { loader_tree } => Route::AppPage { html_endpoint: Vc::upcast( - AppPageEndpoint { - ty: AppPageEndpointType::Html, + AppEndpoint { + ty: AppEndpointType::Page { + ty: AppPageEndpointType::Html, + loader_tree, + }, + app_project, + pathname: pathname.clone(), } .cell(), ), rsc_endpoint: Vc::upcast( - AppPageEndpoint { - ty: AppPageEndpointType::Rsc, + AppEndpoint { + ty: AppEndpointType::Page { + ty: AppPageEndpointType::Rsc, + loader_tree, + }, + app_project, + pathname, } .cell(), ), }, - Entrypoint::AppRoute { .. } => Route::AppRoute { - endpoint: Vc::upcast(AppRouteEndpoint.cell()), + AppEntrypoint::AppRoute { path } => Route::AppRoute { + endpoint: Vc::upcast( + AppEndpoint { + ty: AppEndpointType::Route { path }, + app_project, + pathname: pathname.clone(), + } + .cell(), + ), }, } .cell() @@ -34,36 +355,288 @@ enum AppPageEndpointType { Rsc, } +#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Debug, TraceRawVcs)] +enum AppEndpointType { + Page { + ty: AppPageEndpointType, + loader_tree: Vc, + }, + Route { + path: Vc, + }, +} + #[turbo_tasks::value] -struct AppPageEndpoint { - ty: AppPageEndpointType, +struct AppEndpoint { + ty: AppEndpointType, + app_project: Vc, + pathname: String, } #[turbo_tasks::value_impl] -impl Endpoint for AppPageEndpoint { +impl AppEndpoint { #[turbo_tasks::function] - fn write_to_disk(&self) -> Vc { - todo!() + fn client_relative_path(&self) -> Vc { + self.app_project + .project() + .client_root() + .join("_next".to_string()) } #[turbo_tasks::function] - fn changed(&self) -> Vc { - todo!() + fn app_page_entry(&self, loader_tree: Vc) -> Vc { + get_app_page_entry( + self.app_project.rsc_module_context(), + loader_tree, + self.app_project.app_dir(), + self.pathname.clone(), + self.app_project.project().project_path(), + ) } -} -#[turbo_tasks::value] -struct AppRouteEndpoint; + #[turbo_tasks::function] + fn app_route_entry(&self, path: Vc) -> Vc { + get_app_route_entry( + self.app_project.rsc_module_context(), + Vc::upcast(FileSource::new(path)), + self.pathname.clone(), + self.app_project.project().project_path(), + ) + } + + #[turbo_tasks::function] + async fn output(self: Vc) -> Result> { + let this = self.await?; + + let app_entry = match this.ty { + AppEndpointType::Page { ty: _, loader_tree } => self.app_page_entry(loader_tree), + // NOTE(alexkirsz) For routes, technically, a lot of the following code is not needed, + // as we know we won't have any client references. However, for now, for simplicity's + // sake, we just do the same thing as for pages. + AppEndpointType::Route { path } => self.app_route_entry(path), + }; + + let node_root = this.app_project.project().node_root(); + + let client_relative_path = self.client_relative_path(); + let client_relative_path_ref = client_relative_path.await?; + + let server_path = node_root.join("server".to_string()); + + let mut output_assets = vec![]; + + let client_shared_chunks = get_app_client_shared_chunks( + this.app_project.client_runtime_entries(), + this.app_project.project().client_chunking_context(), + ); + + let mut client_shared_chunks_paths = vec![]; + for chunk in client_shared_chunks.await?.iter().copied() { + output_assets.push(chunk); + + let chunk_path = chunk.ident().path().await?; + if chunk_path.extension_ref() == Some("js") { + if let Some(chunk_path) = client_relative_path_ref.get_path_to(&chunk_path) { + client_shared_chunks_paths.push(chunk_path.to_string()); + } + } + } + + let app_entry = app_entry.await?; + let rsc_entry = app_entry.rsc_entry; + + let rsc_entry_asset = Vc::upcast(rsc_entry); + let client_reference_graph = ClientReferenceGraph::new(Vc::cell(vec![rsc_entry_asset])); + let client_reference_types = client_reference_graph.types(); + let client_references = client_reference_graph.entry(rsc_entry_asset); + + let app_ssr_entries: Vec<_> = client_reference_types + .await? + .iter() + .map(|client_reference_ty| async move { + let ClientReferenceType::EcmascriptClientReference(entry) = client_reference_ty + else { + return Ok(None); + }; + + Ok(Some(entry.await?.ssr_module)) + }) + .try_join() + .await? + .into_iter() + .flatten() + .collect(); + + let app_node_entries: Vec<_> = app_ssr_entries.iter().copied().chain([rsc_entry]).collect(); + + // TODO(alexkirsz) Handle dynamic entries and dynamic chunks. + let _dynamic_entries = NextDynamicEntries::from_entries(Vc::cell( + app_node_entries.iter().copied().map(Vc::upcast).collect(), + )) + .await?; + + let rsc_chunk = this + .app_project + .project() + .rsc_chunking_context() + .entry_chunk( + server_path.join(format!( + "app/{original_name}.js", + original_name = app_entry.original_name + )), + app_entry.rsc_entry, + this.app_project.rsc_runtime_entries(), + ); + output_assets.push(rsc_chunk); + + let app_entry_client_references = client_reference_graph + .entry(Vc::upcast(app_entry.rsc_entry)) + .await?; + + let client_references_chunks = get_app_client_references_chunks( + client_reference_types, + this.app_project.project().client_chunking_context(), + this.app_project.project().ssr_chunking_context(), + ); + let client_references_chunks_ref = client_references_chunks.await?; + + let mut entry_client_chunks = vec![]; + // TODO(alexkirsz) In which manifest does this go? + let mut entry_ssr_chunks = vec![]; + for client_reference in app_entry_client_references.iter() { + let client_reference_chunks = client_references_chunks_ref + .get(client_reference.ty()) + .expect("client reference should have corresponding chunks"); + entry_client_chunks + .extend(client_reference_chunks.client_chunks.await?.iter().copied()); + entry_ssr_chunks.extend(client_reference_chunks.ssr_chunks.await?.iter().copied()); + } + + output_assets.extend(entry_client_chunks.iter().copied()); + output_assets.extend(entry_ssr_chunks.iter().copied()); + + let entry_client_chunks_paths = entry_client_chunks + .iter() + .map(|chunk| chunk.ident().path()) + .try_join() + .await?; + let mut entry_client_chunks_paths: Vec<_> = entry_client_chunks_paths + .iter() + .map(|path| { + client_relative_path_ref + .get_path_to(path) + .expect("asset path should be inside client root") + .to_string() + }) + .collect(); + entry_client_chunks_paths.extend(client_shared_chunks_paths.iter().cloned()); + + let app_build_manifest = AppBuildManifest { + pages: [(app_entry.original_name.clone(), entry_client_chunks_paths)] + .into_iter() + .collect(), + }; + let app_build_manifest_output = Vc::upcast(RawOutput::new(Vc::upcast(VirtualSource::new( + node_root.join("server/app-build-manifest.json".to_string()), + AssetContent::file( + File::from(serde_json::to_string_pretty(&app_build_manifest)?).into(), + ), + )))); + output_assets.push(app_build_manifest_output); + + let app_paths_manifest = AppPathsManifest { + node_server_app_paths: PagesManifest { + pages: [( + app_entry.original_name.clone(), + server_path + .await? + .get_path_to(&*rsc_chunk.ident().path().await?) + .expect("RSC chunk path should be within app paths manifest directory") + .to_string(), + )] + .into_iter() + .collect(), + }, + ..Default::default() + }; + let app_paths_manifest_output = Vc::upcast(RawOutput::new(Vc::upcast(VirtualSource::new( + node_root.join("server/app-paths-manifest.json".to_string()), + AssetContent::file( + File::from(serde_json::to_string_pretty(&app_paths_manifest)?).into(), + ), + )))); + output_assets.push(app_paths_manifest_output); + + let build_manifest = BuildManifest { + root_main_files: client_shared_chunks_paths, + ..Default::default() + }; + let build_manifest_output = Vc::upcast(RawOutput::new(Vc::upcast(VirtualSource::new( + node_root.join("build-manifest.json".to_string()), + AssetContent::file(File::from(serde_json::to_string_pretty(&build_manifest)?).into()), + )))); + output_assets.push(build_manifest_output); + + let entry_manifest = ClientReferenceManifest::build_output( + node_root, + client_relative_path, + app_entry.original_name.clone(), + client_references, + client_references_chunks, + this.app_project.project().client_chunking_context(), + Vc::upcast(this.app_project.project().ssr_chunking_context()), + ); + output_assets.push(entry_manifest); + + Ok(AppEndpointOutput { + rsc_chunk, + output_assets: Vc::cell(output_assets), + } + .cell()) + } +} #[turbo_tasks::value_impl] -impl Endpoint for AppRouteEndpoint { +impl Endpoint for AppEndpoint { #[turbo_tasks::function] - fn write_to_disk(&self) -> Vc { - todo!() + async fn write_to_disk(self: Vc) -> Result> { + let output = self.output(); + + let this = self.await?; + let node_root = this.app_project.project().node_root(); + + let output = output.await?; + let node_root_ref = node_root.await?; + + emit_all_assets( + output.output_assets, + this.app_project.project().node_root(), + self.client_relative_path(), + this.app_project.project().node_root(), + ) + .await?; + + Ok(WrittenEndpoint { + server_entry_path: node_root_ref + .get_path_to(&*output.rsc_chunk.ident().path().await?) + .context("rsc chunk entry path must be inside the node root")? + .to_string(), + server_paths: vec![], + } + .cell()) } #[turbo_tasks::function] - fn changed(&self) -> Vc { - todo!() + async fn changed(self: Vc) -> Result> { + let output = self.output(); + Ok(any_content_changed_of_output_assets( + output.await?.output_assets, + )) } } + +#[turbo_tasks::value] +struct AppEndpointOutput { + rsc_chunk: Vc>, + output_assets: Vc, +} diff --git a/packages/next-swc/crates/next-api/src/entrypoints.rs b/packages/next-swc/crates/next-api/src/entrypoints.rs new file mode 100644 index 0000000000000..751d1d3cd4a76 --- /dev/null +++ b/packages/next-swc/crates/next-api/src/entrypoints.rs @@ -0,0 +1,9 @@ +use indexmap::IndexMap; + +use crate::{project::Middleware, route::Route}; + +#[turbo_tasks::value(shared)] +pub struct Entrypoints { + pub routes: IndexMap, + pub middleware: Option, +} diff --git a/packages/next-swc/crates/next-api/src/lib.rs b/packages/next-swc/crates/next-api/src/lib.rs index 07a27f59112d7..0aac4f947eaa8 100644 --- a/packages/next-swc/crates/next-api/src/lib.rs +++ b/packages/next-swc/crates/next-api/src/lib.rs @@ -3,6 +3,7 @@ #![feature(async_fn_in_trait)] mod app; +mod entrypoints; mod pages; pub mod project; pub mod route; diff --git a/packages/next-swc/crates/next-api/src/pages.rs b/packages/next-swc/crates/next-api/src/pages.rs index 2c54844653430..05ab6866c76b8 100644 --- a/packages/next-swc/crates/next-api/src/pages.rs +++ b/packages/next-swc/crates/next-api/src/pages.rs @@ -2,16 +2,28 @@ use anyhow::{bail, Context, Result}; use indexmap::IndexMap; use next_core::{ create_page_loader_entry_module, emit_all_assets, get_asset_path_from_pathname, - pages_structure::{PagesDirectoryStructure, PagesStructure, PagesStructureItem}, + mode::NextMode, + next_client::{ + get_client_module_options_context, get_client_resolve_options_context, + get_client_runtime_entries, ClientContextType, + }, + next_dynamic::NextDynamicTransition, + next_server::{ + get_server_module_options_context, get_server_resolve_options_context, + get_server_runtime_entries, ServerContextType, + }, + pages_structure::{ + find_pages_structure, PagesDirectoryStructure, PagesStructure, PagesStructureItem, + }, }; use turbo_tasks::{Completion, Completions, Value, Vc}; use turbopack_binding::{ - turbo::tasks_fs::FileSystemPath, + turbo::tasks_fs::{FileSystem, FileSystemPath, VirtualFileSystem}, turbopack::{ core::{ asset::Asset, changed::{any_content_changed, any_content_changed_of_output_assets}, - chunk::{ChunkableModule, ChunkingContext}, + chunk::{ChunkableModule, ChunkingContext, EvaluatableAssets}, context::AssetContext, file_source::FileSource, output::{OutputAsset, OutputAssets}, @@ -19,6 +31,12 @@ use turbopack_binding::{ source::Source, }, ecmascript::EcmascriptModuleAsset, + turbopack::{ + module_options::ModuleOptionsContext, + resolve_options_context::ResolveOptionsContext, + transition::{ContextTransition, TransitionsByName}, + ModuleAssetContext, + }, }, }; @@ -27,77 +45,266 @@ use crate::{ route::{Endpoint, Route, Routes, WrittenEndpoint}, }; -#[turbo_tasks::function] -pub async fn get_pages_routes( +#[turbo_tasks::value] +pub struct PagesProject { project: Vc, - page_structure: Vc, -) -> Result> { - let PagesStructure { api, pages, .. } = *page_structure.await?; - let mut routes = IndexMap::new(); - async fn add_dir_to_routes( - routes: &mut IndexMap, - dir: Vc, - make_route: impl Fn(Vc, Vc, Vc) -> Route, - ) -> Result<()> { - let mut queue = vec![dir]; - while let Some(dir) = queue.pop() { - let PagesDirectoryStructure { - ref items, - ref children, - next_router_path: _, - project_path: _, - } = *dir.await?; - for &item in items.iter() { - let PagesStructureItem { - next_router_path, - project_path, - original_path, - } = *item.await?; - let pathname = format!("/{}", next_router_path.await?.path); - let pathname_vc = Vc::cell(pathname.clone()); - let original_name = Vc::cell(format!("/{}", original_path.await?.path)); - let route = make_route(pathname_vc, original_name, project_path); - routes.insert(pathname, route); - } - for &child in children.iter() { - queue.push(child); + mode: NextMode, +} + +#[turbo_tasks::value_impl] +impl PagesProject { + #[turbo_tasks::function] + pub async fn new(project: Vc, mode: NextMode) -> Result> { + Ok(PagesProject { project, mode }.cell()) + } + + #[turbo_tasks::function] + pub async fn routes(self: Vc) -> Result> { + let PagesStructure { api, pages, .. } = &*self.pages_structure().await?; + let mut routes = IndexMap::new(); + async fn add_dir_to_routes( + routes: &mut IndexMap, + dir: Vc, + make_route: impl Fn(Vc, Vc, Vc) -> Route, + ) -> Result<()> { + let mut queue = vec![dir]; + while let Some(dir) = queue.pop() { + let PagesDirectoryStructure { + ref items, + ref children, + next_router_path: _, + project_path: _, + } = *dir.await?; + for &item in items.iter() { + let PagesStructureItem { + next_router_path, + project_path, + original_path, + } = *item.await?; + let pathname = format!("/{}", next_router_path.await?.path); + let pathname_vc = Vc::cell(pathname.clone()); + let original_name = Vc::cell(format!("/{}", original_path.await?.path)); + let route = make_route(pathname_vc, original_name, project_path); + routes.insert(pathname, route); + } + for &child in children.iter() { + queue.push(child); + } } + Ok(()) + } + if let Some(api) = api { + add_dir_to_routes(&mut routes, *api, |pathname, original_name, path| { + Route::PageApi { + endpoint: Vc::upcast(PageApiEndpoint::new(self, pathname, original_name, path)), + } + }) + .await?; + } + if let Some(page) = pages { + add_dir_to_routes(&mut routes, *page, |pathname, original_name, path| { + Route::Page { + html_endpoint: Vc::upcast(PageHtmlEndpoint::new( + self, + pathname, + original_name, + path, + )), + data_endpoint: Vc::upcast(PageDataEndpoint::new( + self, + pathname, + original_name, + path, + )), + } + }) + .await?; } - Ok(()) + Ok(Vc::cell(routes)) } - if let Some(api) = api { - add_dir_to_routes(&mut routes, api, |pathname, original_name, path| { - Route::PageApi { - endpoint: Vc::upcast(ApiEndpoint::new(project, pathname, original_name, path)), - } - }) - .await?; + + #[turbo_tasks::function] + fn project(&self) -> Vc { + self.project } - if let Some(page) = pages { - add_dir_to_routes(&mut routes, page, |pathname, original_name, path| { - Route::Page { - html_endpoint: Vc::upcast(PageHtmlEndpoint::new( - project, - pathname, - original_name, - path, - )), - data_endpoint: Vc::upcast(PageDataEndpoint::new( - project, - pathname, - original_name, - path, - )), - } + + #[turbo_tasks::function] + fn pages_structure(&self) -> Vc { + let next_router_fs = Vc::upcast::>(VirtualFileSystem::new()); + let next_router_root = next_router_fs.root(); + find_pages_structure( + self.project.project_path(), + next_router_root, + self.project.next_config().page_extensions(), + ) + } + + #[turbo_tasks::function] + async fn pages_dir(self: Vc) -> Result> { + Ok(if let Some(pages) = self.pages_structure().await?.pages { + pages.project_path() + } else { + self.project().project_path().join("pages".to_string()) }) - .await?; } - Ok(Vc::cell(routes)) + + #[turbo_tasks::function] + fn transitions(self: Vc) -> Vc { + Vc::cell( + [( + "next-dynamic".to_string(), + Vc::upcast(NextDynamicTransition::new(self.client_transition())), + )] + .into_iter() + .collect(), + ) + } + + #[turbo_tasks::function] + fn client_transition(self: Vc) -> Vc { + ContextTransition::new( + self.project().client_compile_time_info(), + self.client_module_options_context(), + self.client_resolve_options_context(), + ) + } + + #[turbo_tasks::function] + async fn client_module_options_context(self: Vc) -> Result> { + let this = self.await?; + Ok(get_client_module_options_context( + self.project().project_path(), + self.project().execution_context(), + self.project().client_compile_time_info().environment(), + Value::new(ClientContextType::Pages { + pages_dir: self.pages_dir(), + }), + this.mode, + self.project().next_config(), + )) + } + + #[turbo_tasks::function] + async fn client_resolve_options_context(self: Vc) -> Result> { + let this = self.await?; + Ok(get_client_resolve_options_context( + self.project().project_path(), + Value::new(ClientContextType::Pages { + pages_dir: self.pages_dir(), + }), + this.mode, + self.project().next_config(), + self.project().execution_context(), + )) + } + + #[turbo_tasks::function] + pub(super) fn client_module_context(self: Vc) -> Vc> { + Vc::upcast(ModuleAssetContext::new( + self.transitions(), + self.project().client_compile_time_info(), + self.client_module_options_context(), + self.client_resolve_options_context(), + )) + } + + #[turbo_tasks::function] + pub(super) fn ssr_module_context(self: Vc) -> Vc> { + Vc::upcast(ModuleAssetContext::new( + self.transitions(), + self.project().server_compile_time_info(), + self.ssr_module_options_context(), + self.ssr_resolve_options_context(), + )) + } + + #[turbo_tasks::function] + pub(super) fn ssr_data_module_context(self: Vc) -> Vc> { + Vc::upcast(ModuleAssetContext::new( + self.transitions(), + self.project().server_compile_time_info(), + self.ssr_data_module_options_context(), + self.ssr_resolve_options_context(), + )) + } + + #[turbo_tasks::function] + async fn ssr_module_options_context(self: Vc) -> Result> { + let this = self.await?; + Ok(get_server_module_options_context( + self.project().project_path(), + self.project().execution_context(), + Value::new(ServerContextType::Pages { + pages_dir: self.pages_dir(), + }), + this.mode, + self.project().next_config(), + )) + } + + #[turbo_tasks::function] + async fn ssr_data_module_options_context(self: Vc) -> Result> { + let this = self.await?; + Ok(get_server_module_options_context( + self.project().project_path(), + self.project().execution_context(), + Value::new(ServerContextType::PagesData { + pages_dir: self.pages_dir(), + }), + this.mode, + self.project().next_config(), + )) + } + + #[turbo_tasks::function] + async fn ssr_resolve_options_context(self: Vc) -> Result> { + let this = self.await?; + Ok(get_server_resolve_options_context( + self.project().project_path(), + Value::new(ServerContextType::Pages { + pages_dir: self.pages_dir(), + }), + this.mode, + self.project().next_config(), + self.project().execution_context(), + )) + } + + #[turbo_tasks::function] + async fn client_runtime_entries(self: Vc) -> Result> { + let this = self.await?; + let client_runtime_entries = get_client_runtime_entries( + self.project().project_path(), + self.project().env(), + Value::new(ClientContextType::Pages { + pages_dir: self.pages_dir(), + }), + this.mode, + self.project().next_config(), + self.project().execution_context(), + ); + Ok(client_runtime_entries.resolve_entries(self.client_module_context())) + } + + #[turbo_tasks::function] + async fn ssr_runtime_entries(self: Vc) -> Result> { + let this = self.await?; + let ssr_runtime_entries = get_server_runtime_entries( + self.project().project_path(), + self.project().env(), + Value::new(ServerContextType::Pages { + pages_dir: self.pages_dir(), + }), + this.mode, + self.project().next_config(), + ); + Ok(ssr_runtime_entries.resolve_entries(self.ssr_module_context())) + } } #[turbo_tasks::value] struct PageHtmlEndpoint { - project: Vc, + pages_project: Vc, pathname: Vc, original_name: Vc, path: Vc, @@ -107,13 +314,13 @@ struct PageHtmlEndpoint { impl PageHtmlEndpoint { #[turbo_tasks::function] fn new( - project: Vc, + pages_project: Vc, pathname: Vc, original_name: Vc, path: Vc, ) -> Vc { PageHtmlEndpoint { - project, + pages_project, pathname, original_name, path, @@ -132,7 +339,7 @@ impl PageHtmlEndpoint { let this = self.await?; let client_module = create_page_loader_entry_module( - this.project.pages_client_module_context(), + this.pages_project.client_module_context(), self.source(), this.pathname, ); @@ -143,14 +350,14 @@ impl PageHtmlEndpoint { bail!("expected an ECMAScript module asset"); }; - let client_chunking_context = this.project.client_chunking_context(); + let client_chunking_context = this.pages_project.project().client_chunking_context(); let client_entry_chunk = client_module.as_root_chunk(Vc::upcast(client_chunking_context)); let client_chunks = client_chunking_context.evaluated_chunk_group( client_entry_chunk, - this.project - .pages_client_runtime_entries() + this.pages_project + .client_runtime_entries() .with_entry(Vc::upcast(client_module)), ); @@ -163,8 +370,8 @@ impl PageHtmlEndpoint { let reference_type = Value::new(ReferenceType::Entry(EntryReferenceSubType::Page)); let ssr_module = this - .project - .pages_ssr_module_context() + .pages_project + .ssr_module_context() .process(self.source(), reference_type.clone()); let Some(ssr_module) = @@ -176,12 +383,20 @@ impl PageHtmlEndpoint { let asset_path = get_asset_path_from_pathname(&this.pathname.await?, ".js"); let ssr_entry_chunk_path_string = format!("server/pages{asset_path}"); - let ssr_entry_chunk_path = this.project.node_root().join(ssr_entry_chunk_path_string); - let ssr_entry_chunk = this.project.ssr_chunking_context().entry_chunk( - ssr_entry_chunk_path, - Vc::upcast(ssr_module), - this.project.pages_ssr_runtime_entries(), - ); + let ssr_entry_chunk_path = this + .pages_project + .project() + .node_root() + .join(ssr_entry_chunk_path_string); + let ssr_entry_chunk = this + .pages_project + .project() + .ssr_chunking_context() + .entry_chunk( + ssr_entry_chunk_path, + Vc::upcast(ssr_module), + this.pages_project.ssr_runtime_entries(), + ); Ok(ssr_entry_chunk) } @@ -195,15 +410,15 @@ impl Endpoint for PageHtmlEndpoint { let ssr_chunk = self.ssr_chunk(); let ssr_emit = emit_all_assets( Vc::cell(vec![ssr_chunk]), - this.project.node_root(), - this.project.client_root().join("_next".to_string()), - this.project.node_root(), + this.pages_project.project().node_root(), + this.pages_project.project().client_relative_path(), + this.pages_project.project().node_root(), ); let client_emit = emit_all_assets( self.client_chunks(), - this.project.node_root(), - this.project.client_root().join("_next".to_string()), - this.project.node_root(), + this.pages_project.project().node_root(), + this.pages_project.project().client_relative_path(), + this.pages_project.project().node_root(), ); ssr_emit.await?; @@ -211,7 +426,8 @@ impl Endpoint for PageHtmlEndpoint { Ok(WrittenEndpoint { server_entry_path: this - .project + .pages_project + .project() .node_root() .await? .get_path_to(&*ssr_chunk.ident().path().await?) @@ -234,7 +450,7 @@ impl Endpoint for PageHtmlEndpoint { #[turbo_tasks::value] struct PageDataEndpoint { - project: Vc, + pages_project: Vc, pathname: Vc, original_name: Vc, path: Vc, @@ -244,13 +460,13 @@ struct PageDataEndpoint { impl PageDataEndpoint { #[turbo_tasks::function] fn new( - project: Vc, + pages_project: Vc, pathname: Vc, original_name: Vc, path: Vc, ) -> Vc { PageDataEndpoint { - project, + pages_project, pathname, original_name, path, @@ -270,8 +486,8 @@ impl PageDataEndpoint { let reference_type = Value::new(ReferenceType::Entry(EntryReferenceSubType::Page)); let ssr_data_module = this - .project - .pages_ssr_data_module_context() + .pages_project + .ssr_data_module_context() .process(self.source(), reference_type.clone()); let Some(ssr_data_module) = @@ -284,14 +500,19 @@ impl PageDataEndpoint { let ssr_data_entry_chunk_path_string = format!("server/pages-data/{asset_path}"); let ssr_data_entry_chunk_path = this - .project + .pages_project + .project() .node_root() .join(ssr_data_entry_chunk_path_string); - let ssr_data_entry_chunk = this.project.ssr_data_chunking_context().entry_chunk( - ssr_data_entry_chunk_path, - Vc::upcast(ssr_data_module), - this.project.pages_ssr_runtime_entries(), - ); + let ssr_data_entry_chunk = this + .pages_project + .project() + .ssr_data_chunking_context() + .entry_chunk( + ssr_data_entry_chunk_path, + Vc::upcast(ssr_data_module), + this.pages_project.ssr_runtime_entries(), + ); Ok(ssr_data_entry_chunk) } @@ -305,15 +526,16 @@ impl Endpoint for PageDataEndpoint { let ssr_data_chunk = self.ssr_data_chunk(); emit_all_assets( Vc::cell(vec![ssr_data_chunk]), - this.project.node_root(), - this.project.client_root().join("_next".to_string()), - this.project.node_root(), + this.pages_project.project().node_root(), + this.pages_project.project().client_relative_path(), + this.pages_project.project().node_root(), ) .await?; Ok(WrittenEndpoint { server_entry_path: this - .project + .pages_project + .project() .node_root() .await? .get_path_to(&*ssr_data_chunk.ident().path().await?) @@ -332,41 +554,105 @@ impl Endpoint for PageDataEndpoint { } #[turbo_tasks::value] -struct ApiEndpoint { - project: Vc, +struct PageApiEndpoint { + pages_project: Vc, pathname: Vc, original_name: Vc, path: Vc, } #[turbo_tasks::value_impl] -impl ApiEndpoint { +impl PageApiEndpoint { #[turbo_tasks::function] fn new( - project: Vc, + pages_project: Vc, pathname: Vc, original_name: Vc, path: Vc, ) -> Vc { - ApiEndpoint { - project, + PageApiEndpoint { + pages_project, pathname, original_name, path, } .cell() } + + #[turbo_tasks::function] + fn source(&self) -> Vc> { + Vc::upcast(FileSource::new(self.path)) + } + + #[turbo_tasks::function] + async fn api_chunk(self: Vc) -> Result>> { + let this = self.await?; + let reference_type = Value::new(ReferenceType::Entry(EntryReferenceSubType::PagesApi)); + + let api_module = this + .pages_project + .ssr_module_context() + .process(self.source(), reference_type.clone()); + + let Some(api_module) = + Vc::try_resolve_downcast_type::(api_module).await? + else { + bail!("expected an ECMAScript module asset"); + }; + + let asset_path = get_asset_path_from_pathname(&this.pathname.await?, ".js"); + + let api_entry_chunk_path_string = format!("server/pages/{asset_path}"); + let api_entry_chunk_path = this + .pages_project + .project() + .node_root() + .join(api_entry_chunk_path_string); + let api_entry_chunk = this + .pages_project + .project() + .ssr_chunking_context() + .entry_chunk( + api_entry_chunk_path, + Vc::upcast(api_module), + this.pages_project.ssr_runtime_entries(), + ); + + Ok(api_entry_chunk) + } } #[turbo_tasks::value_impl] -impl Endpoint for ApiEndpoint { +impl Endpoint for PageApiEndpoint { #[turbo_tasks::function] - fn write_to_disk(&self) -> Vc { - todo!() + async fn write_to_disk(self: Vc) -> Result> { + let this = self.await?; + let api_chunk = self.api_chunk(); + emit_all_assets( + Vc::cell(vec![api_chunk]), + this.pages_project.project().node_root(), + this.pages_project.project().client_relative_path(), + this.pages_project.project().node_root(), + ) + .await?; + + Ok(WrittenEndpoint { + server_entry_path: this + .pages_project + .project() + .node_root() + .await? + .get_path_to(&*api_chunk.ident().path().await?) + .context("API chunk entry path must be inside the node root")? + .to_string(), + server_paths: vec![], + } + .cell()) } #[turbo_tasks::function] - fn changed(&self) -> Vc { - todo!() + fn changed(self: Vc) -> Vc { + let api_chunk = self.api_chunk(); + any_content_changed(Vc::upcast(api_chunk)) } } diff --git a/packages/next-swc/crates/next-api/src/project.rs b/packages/next-swc/crates/next-api/src/project.rs index 53d4d5565c766..8fc5dd9b5d187 100644 --- a/packages/next-swc/crates/next-api/src/project.rs +++ b/packages/next-swc/crates/next-api/src/project.rs @@ -3,27 +3,16 @@ use std::path::MAIN_SEPARATOR; use anyhow::{Context, Result}; use indexmap::{map::Entry, IndexMap}; use next_core::{ - app_structure::{find_app_dir, get_entrypoints}, + app_structure::find_app_dir, mode::NextMode, - next_client::{ - get_client_chunking_context, get_client_compile_time_info, - get_client_module_options_context, get_client_resolve_options_context, - get_client_runtime_entries, ClientContextType, - }, + next_client::{get_client_chunking_context, get_client_compile_time_info}, next_config::NextConfig, - next_dynamic::NextDynamicTransition, - next_server::{ - get_server_chunking_context, get_server_compile_time_info, - get_server_module_options_context, get_server_resolve_options_context, - get_server_runtime_entries, ServerContextType, - }, - pages_structure::{find_pages_structure, PagesStructure}, + next_server::{get_server_chunking_context, get_server_compile_time_info}, util::NextSourceConfig, }; use serde::{Deserialize, Serialize}; use turbo_tasks::{ - debug::ValueDebugFormat, trace::TraceRawVcs, unit, TaskInput, TransientValue, TryJoinIterExt, - Value, Vc, + debug::ValueDebugFormat, trace::TraceRawVcs, unit, TaskInput, TransientValue, Vc, }; use turbopack_binding::{ turbo::{ @@ -33,29 +22,21 @@ use turbopack_binding::{ turbopack::{ build::BuildChunkingContext, core::{ - chunk::{ChunkingContext, EvaluatableAssets}, - compile_time_info::CompileTimeInfo, - context::AssetContext, - environment::ServerAddr, + chunk::ChunkingContext, compile_time_info::CompileTimeInfo, environment::ServerAddr, PROJECT_FILESYSTEM_NAME, }, dev::DevChunkingContext, ecmascript::chunk::EcmascriptChunkingContext, env::dotenv::load_env, node::execution_context::ExecutionContext, - turbopack::{ - evaluate_context::node_build_environment, - module_options::ModuleOptionsContext, - resolve_options_context::ResolveOptionsContext, - transition::{ContextTransition, TransitionsByName}, - ModuleAssetContext, - }, + turbopack::evaluate_context::node_build_environment, }, }; use crate::{ - app::app_entry_point_to_route, - pages::get_pages_routes, + app::{AppProject, OptionAppProject}, + entrypoints::Entrypoints, + pages::PagesProject, route::{Endpoint, Route}, }; @@ -85,12 +66,6 @@ pub struct Middleware { pub config: NextSourceConfig, } -#[turbo_tasks::value] -pub struct Entrypoints { - pub routes: IndexMap, - pub middleware: Option, -} - #[turbo_tasks::value] pub struct Project { /// A root path from which all files must be nested under. Trying to access @@ -129,6 +104,24 @@ impl Project { .cell()) } + #[turbo_tasks::function] + async fn app_project(self: Vc) -> Result> { + let this = self.await?; + let app_dir = find_app_dir(self.project_path()).await?; + + Ok(Vc::cell(if let Some(app_dir) = &*app_dir { + Some(AppProject::new(self, *app_dir, this.mode)) + } else { + None + })) + } + + #[turbo_tasks::function] + async fn pages_project(self: Vc) -> Result> { + let this = self.await?; + Ok(PagesProject::new(self, this.mode)) + } + #[turbo_tasks::function] async fn project_fs(self: Vc) -> Result>> { let this = self.await?; @@ -172,7 +165,12 @@ impl Project { } #[turbo_tasks::function] - async fn project_path(self: Vc) -> Result> { + pub(super) fn client_relative_path(self: Vc) -> Vc { + self.client_root().join("_next".to_string()) + } + + #[turbo_tasks::function] + pub(super) async fn project_path(self: Vc) -> Result> { let this = self.await?; let root = self.project_root_path(); let project_relative = this.project_path.strip_prefix(&this.root_path).unwrap(); @@ -184,29 +182,17 @@ impl Project { } #[turbo_tasks::function] - async fn pages_structure(self: Vc) -> Result> { - let this: turbo_tasks::ReadRef = self.await?; - let next_router_fs = Vc::upcast::>(VirtualFileSystem::new()); - let next_router_root = next_router_fs.root(); - Ok(find_pages_structure( - self.project_path(), - next_router_root, - this.next_config.page_extensions(), - )) - } - - #[turbo_tasks::function] - fn env(self: Vc) -> Vc> { + pub(super) fn env(self: Vc) -> Vc> { load_env(self.project_path()) } #[turbo_tasks::function] - async fn next_config(self: Vc) -> Result> { + pub(super) async fn next_config(self: Vc) -> Result> { Ok(self.await?.next_config) } #[turbo_tasks::function] - fn execution_context(self: Vc) -> Vc { + pub(super) fn execution_context(self: Vc) -> Vc { let node_root = self.node_root(); let node_execution_chunking_context = Vc::upcast( @@ -228,16 +214,12 @@ impl Project { } #[turbo_tasks::function] - async fn client_compile_time_info(self: Vc) -> Result> { - let this = self.await?; - Ok(get_client_compile_time_info( - this.mode, - this.browserslist_query.clone(), - )) + pub(super) fn client_compile_time_info(&self) -> Vc { + get_client_compile_time_info(self.mode, self.browserslist_query.clone()) } #[turbo_tasks::function] - async fn server_compile_time_info(self: Vc) -> Result> { + pub(super) async fn server_compile_time_info(self: Vc) -> Result> { let this = self.await?; Ok(get_server_compile_time_info( this.mode, @@ -247,178 +229,6 @@ impl Project { )) } - #[turbo_tasks::function] - async fn pages_dir(self: Vc) -> Result> { - Ok(if let Some(pages) = self.pages_structure().await?.pages { - pages.project_path() - } else { - self.project_path().join("pages".to_string()) - }) - } - - #[turbo_tasks::function] - fn pages_transitions(self: Vc) -> Vc { - Vc::cell( - [( - "next-dynamic".to_string(), - Vc::upcast(NextDynamicTransition::new(self.pages_client_transition())), - )] - .into_iter() - .collect(), - ) - } - - #[turbo_tasks::function] - fn pages_client_transition(self: Vc) -> Vc { - ContextTransition::new( - self.client_compile_time_info(), - self.pages_client_module_options_context(), - self.pages_client_resolve_options_context(), - ) - } - - #[turbo_tasks::function] - async fn pages_client_module_options_context( - self: Vc, - ) -> Result> { - let this = self.await?; - Ok(get_client_module_options_context( - self.project_path(), - self.execution_context(), - self.client_compile_time_info().environment(), - Value::new(ClientContextType::Pages { - pages_dir: self.pages_dir(), - }), - this.mode, - self.next_config(), - )) - } - - #[turbo_tasks::function] - async fn pages_client_resolve_options_context( - self: Vc, - ) -> Result> { - let this = self.await?; - Ok(get_client_resolve_options_context( - self.project_path(), - Value::new(ClientContextType::Pages { - pages_dir: self.pages_dir(), - }), - this.mode, - self.next_config(), - self.execution_context(), - )) - } - - #[turbo_tasks::function] - pub(super) fn pages_client_module_context(self: Vc) -> Vc> { - Vc::upcast(ModuleAssetContext::new( - self.pages_transitions(), - self.client_compile_time_info(), - self.pages_client_module_options_context(), - self.pages_client_resolve_options_context(), - )) - } - - #[turbo_tasks::function] - pub(super) fn pages_ssr_module_context(self: Vc) -> Vc> { - Vc::upcast(ModuleAssetContext::new( - self.pages_transitions(), - self.server_compile_time_info(), - self.pages_ssr_module_options_context(), - self.pages_ssr_resolve_options_context(), - )) - } - - #[turbo_tasks::function] - pub(super) fn pages_ssr_data_module_context(self: Vc) -> Vc> { - Vc::upcast(ModuleAssetContext::new( - self.pages_transitions(), - self.server_compile_time_info(), - self.pages_ssr_data_module_options_context(), - self.pages_ssr_resolve_options_context(), - )) - } - - #[turbo_tasks::function] - async fn pages_ssr_module_options_context(self: Vc) -> Result> { - let this = self.await?; - Ok(get_server_module_options_context( - self.project_path(), - self.execution_context(), - Value::new(ServerContextType::Pages { - pages_dir: self.pages_dir(), - }), - this.mode, - self.next_config(), - )) - } - - #[turbo_tasks::function] - async fn pages_ssr_data_module_options_context( - self: Vc, - ) -> Result> { - let this = self.await?; - Ok(get_server_module_options_context( - self.project_path(), - self.execution_context(), - Value::new(ServerContextType::PagesData { - pages_dir: self.pages_dir(), - }), - this.mode, - self.next_config(), - )) - } - - #[turbo_tasks::function] - async fn pages_ssr_resolve_options_context( - self: Vc, - ) -> Result> { - let this = self.await?; - Ok(get_server_resolve_options_context( - self.project_path(), - Value::new(ServerContextType::Pages { - pages_dir: self.pages_dir(), - }), - this.mode, - self.next_config(), - self.execution_context(), - )) - } - - #[turbo_tasks::function] - pub(super) async fn pages_client_runtime_entries( - self: Vc, - ) -> Result> { - let this = self.await?; - let client_runtime_entries = get_client_runtime_entries( - self.project_path(), - self.env(), - Value::new(ClientContextType::Pages { - pages_dir: self.pages_dir(), - }), - this.mode, - self.next_config(), - self.execution_context(), - ); - Ok(client_runtime_entries.resolve_entries(self.pages_client_module_context())) - } - - #[turbo_tasks::function] - pub(super) async fn pages_ssr_runtime_entries(self: Vc) -> Result> { - let this = self.await?; - let ssr_runtime_entries = get_server_runtime_entries( - self.project_path(), - self.env(), - Value::new(ServerContextType::Pages { - pages_dir: self.pages_dir(), - }), - this.mode, - self.next_config(), - ); - Ok(ssr_runtime_entries.resolve_entries(self.pages_ssr_module_context())) - } - #[turbo_tasks::function] pub(super) async fn client_chunking_context( self: Vc, @@ -474,25 +284,16 @@ impl Project { /// provided page_extensions). #[turbo_tasks::function] pub async fn entrypoints(self: Vc) -> Result> { - let this = self.await?; let mut routes = IndexMap::new(); - if let Some(app_dir) = *find_app_dir(self.project_path()).await? { - let app_entrypoints = get_entrypoints(app_dir, this.next_config.page_extensions()); - routes.extend( - app_entrypoints - .await? - .iter() - .map(|(pathname, app_entrypoint)| async { - Ok(( - pathname.clone(), - *app_entry_point_to_route(*app_entrypoint).await?, - )) - }) - .try_join() - .await?, - ); + let app_project = self.app_project(); + let pages_project = self.pages_project(); + + if let Some(app_project) = &*app_project.await? { + let app_routes = app_project.routes(); + routes.extend(app_routes.await?.iter().map(|(k, v)| (k.clone(), *v))); } - for (pathname, page_route) in get_pages_routes(self, self.pages_structure()).await?.iter() { + + for (pathname, page_route) in pages_project.routes().await?.iter() { match routes.entry(pathname.clone()) { Entry::Occupied(mut entry) => { *entry.get_mut() = Route::Conflict; @@ -502,6 +303,7 @@ impl Project { } } } + // TODO middleware Ok(Entrypoints { routes, diff --git a/packages/next-swc/crates/next-build/src/lib.rs b/packages/next-swc/crates/next-build/src/lib.rs index f522ea28e31b5..afe7ef263d2d9 100644 --- a/packages/next-swc/crates/next-build/src/lib.rs +++ b/packages/next-swc/crates/next-build/src/lib.rs @@ -8,7 +8,6 @@ use turbopack_binding::turbo::{ }; pub mod build_options; -pub mod manifests; pub(crate) mod next_app; pub(crate) mod next_build; pub(crate) mod next_pages; diff --git a/packages/next-swc/crates/next-build/src/next_app/app_entries.rs b/packages/next-swc/crates/next-build/src/next_app/app_entries.rs index 7d063e0e72372..1fc853d037dc6 100644 --- a/packages/next-swc/crates/next-build/src/next_app/app_entries.rs +++ b/packages/next-swc/crates/next-build/src/next_app/app_entries.rs @@ -1,78 +1,44 @@ use std::collections::HashMap; -use anyhow::{Context, Result}; -use indexmap::IndexMap; -use indoc::formatdoc; +use anyhow::Result; use next_core::{ app_structure::{find_app_dir_if_enabled, get_entrypoints, get_global_metadata, Entrypoint}, mode::NextMode, + next_app::{ + get_app_client_shared_chunks, get_app_page_entry, get_app_route_entry, + get_app_route_favicon_entry, AppEntry, ClientReferencesChunks, + }, next_client::{ get_client_module_options_context, get_client_resolve_options_context, get_client_runtime_entries, ClientContextType, }, - next_client_reference::{ - ClientReference, ClientReferenceType, NextEcmascriptClientReferenceTransition, - }, + next_client_reference::{ClientReferenceGraph, NextEcmascriptClientReferenceTransition}, next_config::NextConfig, next_dynamic::NextDynamicTransition, + next_manifests::{AppBuildManifest, AppPathsManifest, BuildManifest, ClientReferenceManifest}, next_server::{ get_server_module_options_context, get_server_resolve_options_context, get_server_runtime_entries, ServerContextType, }, }; -use turbo_tasks::{TryJoinIterExt, Value, ValueToString, Vc}; +use turbo_tasks::{TryJoinIterExt, Value, Vc}; use turbopack_binding::{ turbo::{ tasks_env::{CustomProcessEnv, ProcessEnv}, - tasks_fs::{File, FileSystemPath}, + tasks_fs::FileSystemPath, }, turbopack::{ build::BuildChunkingContext, core::{ - asset::{Asset, AssetContent}, - chunk::{ - availability_info::AvailabilityInfo, ChunkingContext, EvaluatableAssets, - ModuleId as TurbopackModuleId, - }, - compile_time_info::CompileTimeInfo, - file_source::FileSource, - output::{OutputAsset, OutputAssets}, - raw_output::RawOutput, - virtual_source::VirtualSource, - }, - ecmascript::{ - chunk::{ - EcmascriptChunk, EcmascriptChunkItemExt, EcmascriptChunkPlaceable, - EcmascriptChunkingContext, - }, - utils::StringifyJs, + asset::Asset, chunk::EvaluatableAssets, compile_time_info::CompileTimeInfo, + file_source::FileSource, output::OutputAsset, }, + ecmascript::chunk::EcmascriptChunkingContext, node::execution_context::ExecutionContext, turbopack::{transition::ContextTransition, ModuleAssetContext}, }, }; -use super::{ - app_client_reference::ClientReferenceChunks, app_page_entry::get_app_page_entry, - app_route_entry::get_app_route_entry, app_route_favicon_entry::get_app_route_favicon_entry, -}; -use crate::manifests::{ - AppBuildManifest, AppPathsManifest, BuildManifest, ClientReferenceManifest, ManifestNode, - ManifestNodeEntry, ModuleId, -}; - -/// The entry module asset for a Next.js app route or page. -#[turbo_tasks::value(shared)] -pub struct AppEntry { - /// The pathname of the route or page. - pub pathname: String, - /// The original Next.js name of the route or page. This is used instead of - /// the pathname to refer to this entry. - pub original_name: String, - /// The RSC module asset for the route or page. - pub rsc_entry: Vc>, -} - #[turbo_tasks::value] pub struct AppEntries { /// All app entries. @@ -223,19 +189,19 @@ pub async fn get_app_entries( .iter() .map(|(pathname, entrypoint)| async move { Ok(match entrypoint { - Entrypoint::AppPage { loader_tree } => { - get_app_page_entry(rsc_context, *loader_tree, app_dir, pathname, project_root) - .await? - } - Entrypoint::AppRoute { path } => { - get_app_route_entry( - rsc_context, - Vc::upcast(FileSource::new(*path)), - pathname, - project_root, - ) - .await? - } + Entrypoint::AppPage { loader_tree } => get_app_page_entry( + rsc_context, + *loader_tree, + app_dir, + pathname.clone(), + project_root, + ), + Entrypoint::AppRoute { path } => get_app_route_entry( + rsc_context, + Vc::upcast(FileSource::new(*path)), + pathname.clone(), + project_root, + ), }) }) .try_join() @@ -245,7 +211,11 @@ pub async fn get_app_entries( let global_metadata = global_metadata.await?; if let Some(favicon) = global_metadata.favicon { - entries.push(get_app_route_favicon_entry(rsc_context, favicon, project_root).await?); + entries.push(get_app_route_favicon_entry( + rsc_context, + favicon, + project_root, + )); } let client_context = ModuleAssetContext::new( @@ -276,20 +246,20 @@ pub async fn get_app_entries( /// manifests. pub async fn compute_app_entries_chunks( app_entries: &AppEntries, - app_client_references_by_entry: &IndexMap>, Vec>, - app_client_references_chunks: &IndexMap, + app_client_reference_graph: Vc, + app_client_references_chunks: Vc, rsc_chunking_context: Vc, client_chunking_context: Vc>, ssr_chunking_context: Vc>, node_root: Vc, - client_relative_path: &FileSystemPath, + client_relative_path: Vc, app_paths_manifest_dir_path: &FileSystemPath, app_build_manifest: &mut AppBuildManifest, build_manifest: &mut BuildManifest, app_paths_manifest: &mut AppPathsManifest, all_chunks: &mut Vec>>, ) -> Result<()> { - let node_root_ref = node_root.await?; + let client_relative_path_ref = client_relative_path.await?; let app_client_shared_chunks = get_app_client_shared_chunks(app_entries.client_runtime_entries, client_chunking_context); @@ -300,19 +270,21 @@ pub async fn compute_app_entries_chunks( let chunk_path = chunk.ident().path().await?; if chunk_path.extension_ref() == Some("js") { - if let Some(chunk_path) = client_relative_path.get_path_to(&chunk_path) { + if let Some(chunk_path) = client_relative_path.await?.get_path_to(&chunk_path) { app_shared_client_chunks_paths.push(chunk_path.to_string()); build_manifest.root_main_files.push(chunk_path.to_string()); } } } + let app_client_references_chunks_ref = app_client_references_chunks.await?; + for app_entry in app_entries.entries.iter().copied() { let app_entry = app_entry.await?; - let app_entry_client_references = app_client_references_by_entry - .get(&Vc::upcast(app_entry.rsc_entry)) - .expect("app entry should have a corresponding client references list"); + let app_entry_client_references = app_client_reference_graph + .entry(Vc::upcast(app_entry.rsc_entry)) + .await?; let rsc_chunk = rsc_chunking_context.entry_chunk( node_root.join(format!( @@ -325,10 +297,11 @@ pub async fn compute_app_entries_chunks( all_chunks.push(rsc_chunk); let mut app_entry_client_chunks = vec![]; + // TODO(alexkirsz) In which manifest should this go? let mut app_entry_ssr_chunks = vec![]; - for client_reference in app_entry_client_references { - let client_reference_chunks = app_client_references_chunks + for client_reference in app_entry_client_references.iter() { + let client_reference_chunks = app_client_references_chunks_ref .get(client_reference.ty()) .expect("client reference should have corresponding chunks"); app_entry_client_chunks @@ -344,7 +317,7 @@ pub async fn compute_app_entries_chunks( let mut app_entry_client_chunks_paths: Vec<_> = app_entry_client_chunks_paths .iter() .map(|path| { - client_relative_path + client_relative_path_ref .get_path_to(path) .expect("asset path should be inside client root") .to_string() @@ -365,232 +338,18 @@ pub async fn compute_app_entries_chunks( .to_string(), ); - let mut entry_manifest: ClientReferenceManifest = Default::default(); - - for app_client_reference in app_entry_client_references { - let app_client_reference_ty = app_client_reference.ty(); - - let app_client_reference_chunks = app_client_references_chunks - .get(app_client_reference.ty()) - .context("client reference chunks not found")?; - - let client_reference_chunks = app_client_references_chunks - .get(app_client_reference_ty) - .context("client reference chunks not found")?; - let client_chunks = &client_reference_chunks.client_chunks.await?; - - let client_chunks_paths = client_chunks - .iter() - .map(|chunk| chunk.ident().path()) - .try_join() - .await?; - - if let Some(server_component) = app_client_reference.server_component() { - let server_component_name = server_component - .server_path() - .with_extension("".to_string()) - .to_string() - .await?; - - let entry_css_files = entry_manifest - .entry_css_files - .entry(server_component_name.clone_value()) - .or_insert_with(Default::default); - - match app_client_reference_ty { - ClientReferenceType::CssClientReference(_) => { - entry_css_files.extend( - client_chunks_paths - .iter() - .filter_map(|chunk_path| { - client_relative_path.get_path_to(chunk_path) - }) - .map(ToString::to_string), - ); - } - - ClientReferenceType::EcmascriptClientReference(_) => { - entry_css_files.extend( - client_chunks_paths - .iter() - .filter_map(|chunk_path| { - if chunk_path.extension_ref() == Some("css") { - client_relative_path.get_path_to(chunk_path) - } else { - None - } - }) - .map(ToString::to_string), - ); - } - } - } - - match app_client_reference_ty { - ClientReferenceType::CssClientReference(_) => {} - - ClientReferenceType::EcmascriptClientReference(ecmascript_client_reference) => { - let client_chunks = &app_client_reference_chunks.client_chunks.await?; - let ssr_chunks = &app_client_reference_chunks.ssr_chunks.await?; - - let ecmascript_client_reference = ecmascript_client_reference.await?; - - let client_module_id = ecmascript_client_reference - .client_module - .as_chunk_item(client_chunking_context) - .id() - .await?; - let ssr_module_id = ecmascript_client_reference - .ssr_module - .as_chunk_item(ssr_chunking_context) - .id() - .await?; - - let server_path = ecmascript_client_reference - .server_ident - .path() - .to_string() - .await?; - - let client_chunks_paths = client_chunks - .iter() - .map(|chunk| chunk.ident().path()) - .try_join() - .await?; - let client_chunks_paths: Vec = client_chunks_paths - .iter() - .filter_map(|chunk_path| client_relative_path.get_path_to(chunk_path)) - .map(ToString::to_string) - .collect::>(); - - let ssr_chunks_paths = ssr_chunks - .iter() - .map(|chunk| chunk.ident().path()) - .try_join() - .await?; - let ssr_chunks_paths = ssr_chunks_paths - .iter() - .filter_map(|chunk_path| node_root_ref.get_path_to(chunk_path)) - .map(ToString::to_string) - .collect::>(); - - let mut ssr_manifest_node = ManifestNode::default(); - - entry_manifest.client_modules.module_exports.insert( - get_client_reference_module_key(&server_path, "*"), - ManifestNodeEntry { - name: "*".to_string(), - id: (&*client_module_id).into(), - chunks: client_chunks_paths.clone(), - // TODO(WEB-434) - r#async: false, - }, - ); - - ssr_manifest_node.module_exports.insert( - "*".to_string(), - ManifestNodeEntry { - name: "*".to_string(), - id: (&*ssr_module_id).into(), - chunks: ssr_chunks_paths.clone(), - // TODO(WEB-434) - r#async: false, - }, - ); - - entry_manifest - .ssr_module_mapping - .insert((&*client_module_id).into(), ssr_manifest_node); - } - } - } - - let client_reference_manifest_json = serde_json::to_string(&entry_manifest).unwrap(); - let client_reference_manifest_source = VirtualSource::new( - node_root.join(format!( - "server/app/{original_name}_client-reference-manifest.js", - original_name = app_entry.original_name - )), - AssetContent::file( - File::from(formatdoc! { - r#" - globalThis.__RSC_MANIFEST = globalThis.__RSC_MANIFEST || {{}}; - globalThis.__RSC_MANIFEST[{original_name}] = {manifest} - "#, - original_name = StringifyJs(&app_entry.original_name), - manifest = StringifyJs(&client_reference_manifest_json) - }) - .into(), - ), + let entry_manifest = ClientReferenceManifest::build_output( + node_root, + client_relative_path, + app_entry.original_name.clone(), + app_client_reference_graph.entry(Vc::upcast(app_entry.rsc_entry)), + app_client_references_chunks, + client_chunking_context, + ssr_chunking_context, ); - all_chunks.push(Vc::upcast(RawOutput::new(Vc::upcast( - client_reference_manifest_source, - )))); - } - Ok(()) -} - -#[turbo_tasks::function] -pub async fn get_app_shared_client_chunk( - app_client_runtime_entries: Vc, - client_chunking_context: Vc>, -) -> Result> { - let client_runtime_entries: Vec<_> = app_client_runtime_entries - .await? - .iter() - .map(|entry| async move { - Ok(Vc::try_resolve_sidecast::>(*entry).await?) - }) - .try_join() - .await? - .into_iter() - .flatten() - .collect(); - - Ok(EcmascriptChunk::new_normalized( - client_chunking_context, - // TODO(alexkirsz) Should this accept Evaluatable instead? - Vc::cell(client_runtime_entries), - None, - Value::new(AvailabilityInfo::Untracked), - )) -} - -#[turbo_tasks::function] -pub async fn get_app_client_shared_chunks( - app_client_runtime_entries: Vc, - client_chunking_context: Vc>, -) -> Result> { - if app_client_runtime_entries.await?.is_empty() { - return Ok(OutputAssets::empty()); - } - - let app_client_shared_chunk = - get_app_shared_client_chunk(app_client_runtime_entries, client_chunking_context); - - let app_client_shared_chunks = client_chunking_context.evaluated_chunk_group( - Vc::upcast(app_client_shared_chunk), - app_client_runtime_entries, - ); - - Ok(app_client_shared_chunks) -} - -/// See next.js/packages/next/src/lib/client-reference.ts -fn get_client_reference_module_key(server_path: &str, export_name: &str) -> String { - if export_name == "*" { - server_path.to_string() - } else { - format!("{}#{}", server_path, export_name) + all_chunks.push(entry_manifest); } -} -impl From<&TurbopackModuleId> for ModuleId { - fn from(module_id: &TurbopackModuleId) -> Self { - match module_id { - TurbopackModuleId::String(string) => ModuleId::String(string.clone()), - TurbopackModuleId::Number(number) => ModuleId::Number(*number as _), - } - } + Ok(()) } diff --git a/packages/next-swc/crates/next-build/src/next_app/mod.rs b/packages/next-swc/crates/next-build/src/next_app/mod.rs index 06c9a1f43cba7..ab95d6dfd8021 100644 --- a/packages/next-swc/crates/next-build/src/next_app/mod.rs +++ b/packages/next-swc/crates/next-build/src/next_app/mod.rs @@ -1,5 +1 @@ -pub(crate) mod app_client_reference; pub(crate) mod app_entries; -pub(crate) mod app_page_entry; -pub(crate) mod app_route_entry; -pub(crate) mod app_route_favicon_entry; diff --git a/packages/next-swc/crates/next-build/src/next_build.rs b/packages/next-swc/crates/next-build/src/next_build.rs index ecb14d3a18ec7..1147cb33c497d 100644 --- a/packages/next-swc/crates/next-build/src/next_build.rs +++ b/packages/next-swc/crates/next-build/src/next_build.rs @@ -8,10 +8,16 @@ use anyhow::{anyhow, bail, Context, Result}; use dunce::canonicalize; use next_core::{ mode::NextMode, + next_app::get_app_client_references_chunks, next_client::{get_client_chunking_context, get_client_compile_time_info}, - next_client_reference::{ClientReferenceType, ClientReferencesByEntry}, + next_client_reference::{ClientReferenceGraph, ClientReferenceType}, next_config::load_next_config, next_dynamic::NextDynamicEntries, + next_manifests::{ + AppBuildManifest, AppPathsManifest, BuildManifest, ClientBuildManifest, FontManifest, + MiddlewaresManifest, NextFontManifest, PagesManifest, ReactLoadableManifest, + ServerReferenceManifest, + }, next_server::{get_server_chunking_context, get_server_compile_time_info}, url_node::get_sorted_routes, {self}, @@ -45,15 +51,7 @@ use turbopack_binding::{ use crate::{ build_options::{BuildContext, BuildOptions}, - manifests::{ - AppBuildManifest, AppPathsManifest, BuildManifest, ClientBuildManifest, FontManifest, - MiddlewaresManifest, NextFontManifest, PagesManifest, ReactLoadableManifest, - ServerReferenceManifest, - }, - next_app::{ - app_client_reference::compute_app_client_references_chunks, - app_entries::{compute_app_entries_chunks, get_app_entries}, - }, + next_app::app_entries::{compute_app_entries_chunks, get_app_entries}, next_pages::page_entries::{compute_page_entries_chunks, get_page_entries}, }; @@ -167,26 +165,16 @@ pub(crate) async fn next_build(options: TransientInstance) -> Resu .try_join() .await?; - let app_client_references_by_entry = ClientReferencesByEntry::new(Vc::cell( + let app_client_references = ClientReferenceGraph::new(Vc::cell( app_rsc_entries.iter().copied().map(Vc::upcast).collect(), - )) - .await?; - - let app_client_references: HashSet<_> = app_client_references_by_entry - .values() - .flatten() - .copied() - .collect(); + )); // The same client reference can occur from two different server components. // Here, we're only interested in deduped client references. - let app_client_reference_tys: HashSet<_> = app_client_references - .iter() - .map(|client_reference| client_reference.ty()) - .copied() - .collect(); + let app_client_reference_tys = app_client_references.types(); let app_ssr_entries: Vec<_> = app_client_reference_tys + .await? .iter() .map(|client_reference_ty| async move { let ClientReferenceType::EcmascriptClientReference(entry) = client_reference_ty else { @@ -298,26 +286,32 @@ pub(crate) async fn next_build(options: TransientInstance) -> Resu // APP CLIENT REFERENCES CHUNKING - let app_client_references_chunks = compute_app_client_references_chunks( - &app_client_reference_tys, + let app_client_references_chunks = get_app_client_references_chunks( + app_client_reference_tys, client_chunking_context, ssr_chunking_context, - &mut all_chunks, - ) - .await?; + ); + let app_client_references_chunks_ref = app_client_references_chunks.await?; + + for app_client_reference_chunks in app_client_references_chunks_ref.values() { + let client_chunks = &app_client_reference_chunks.client_chunks.await?; + let ssr_chunks = &app_client_reference_chunks.ssr_chunks.await?; + all_chunks.extend(client_chunks.iter().copied()); + all_chunks.extend(ssr_chunks.iter().copied()); + } // APP RSC CHUNKING // TODO(alexkirsz) Do some of that in parallel with the above. compute_app_entries_chunks( &app_entries, - &app_client_references_by_entry, - &app_client_references_chunks, + app_client_references, + app_client_references_chunks, rsc_chunking_context, client_chunking_context, Vc::upcast(ssr_chunking_context), node_root, - &client_relative_path_ref, + client_relative_path, &app_paths_manifest_dir_path, &mut app_build_manifest, &mut build_manifest, diff --git a/packages/next-swc/crates/next-build/src/next_pages/page_entries.rs b/packages/next-swc/crates/next-build/src/next_pages/page_entries.rs index 622e7f6dcbbce..421e72ac45c75 100644 --- a/packages/next-swc/crates/next-build/src/next_pages/page_entries.rs +++ b/packages/next-swc/crates/next-build/src/next_pages/page_entries.rs @@ -8,6 +8,7 @@ use next_core::{ }, next_config::NextConfig, next_dynamic::NextDynamicTransition, + next_manifests::{BuildManifest, PagesManifest}, next_server::{ get_server_module_options_context, get_server_resolve_options_context, get_server_runtime_entries, ServerContextType, @@ -41,8 +42,6 @@ use turbopack_binding::{ }, }; -use crate::manifests::{BuildManifest, PagesManifest}; - #[turbo_tasks::value] pub struct PageEntries { pub entries: Vec>, diff --git a/packages/next-swc/crates/next-core/Cargo.toml b/packages/next-swc/crates/next-core/Cargo.toml index 96d1e848ca324..5b50aff1185cf 100644 --- a/packages/next-swc/crates/next-core/Cargo.toml +++ b/packages/next-swc/crates/next-core/Cargo.toml @@ -12,6 +12,7 @@ bench = false anyhow = { workspace = true } async-recursion = { workspace = true } async-trait = { workspace = true } +base64 = "0.21.0" const_format = "0.2.30" once_cell = { workspace = true } qstring = { workspace = true } @@ -20,6 +21,7 @@ serde = { workspace = true } serde_json = { workspace = true } indexmap = { workspace = true, features = ["serde"] } mime = { workspace = true } +mime_guess = "2.0.4" indoc = { workspace = true } allsorts = { workspace = true } futures = { workspace = true } diff --git a/packages/next-swc/crates/next-core/js/package.json b/packages/next-swc/crates/next-core/js/package.json index b11bcbe69dfa6..1c9b53cc688dd 100644 --- a/packages/next-swc/crates/next-core/js/package.json +++ b/packages/next-swc/crates/next-core/js/package.json @@ -10,8 +10,8 @@ "check": "tsc --noEmit" }, "dependencies": { - "@vercel/turbopack-ecmascript-runtime": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230716.2", - "@vercel/turbopack-node": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230716.2", + "@vercel/turbopack-ecmascript-runtime": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230717.2", + "@vercel/turbopack-node": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230717.2", "anser": "^2.1.1", "css.escape": "^1.5.1", "next": "*", diff --git a/packages/next-swc/crates/next-core/src/app_source.rs b/packages/next-swc/crates/next-core/src/app_source.rs index 55a053ac9d520..f1a6109e615b6 100644 --- a/packages/next-swc/crates/next-core/src/app_source.rs +++ b/packages/next-swc/crates/next-core/src/app_source.rs @@ -65,6 +65,7 @@ use crate::{ fallback::get_fallback_page, loader_tree::{LoaderTreeModule, ServerComponentTransition}, mode::NextMode, + next_app::UnsupportedDynamicMetadataIssue, next_client::{ context::{ get_client_assets_path, get_client_module_options_context, @@ -89,7 +90,6 @@ use crate::{ get_server_resolve_options_context, ServerContextType, }, util::{render_data, NextRuntime}, - UnsupportedDynamicMetadataIssue, }; fn pathname_to_segments(pathname: &str) -> Result<(Vec, RouteType)> { diff --git a/packages/next-swc/crates/next-core/src/app_structure.rs b/packages/next-swc/crates/next-core/src/app_structure.rs index bcaa162f1f9b4..58bdf032ea7e5 100644 --- a/packages/next-swc/crates/next-core/src/app_structure.rs +++ b/packages/next-swc/crates/next-core/src/app_structure.rs @@ -94,7 +94,7 @@ pub enum MetadataWithAltItem { } /// A single metadata file. -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, TraceRawVcs)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, TaskInput, TraceRawVcs)] pub enum MetadataItem { Static { path: Vc }, Dynamic { path: Vc }, diff --git a/packages/next-swc/crates/next-core/src/manifest.rs b/packages/next-swc/crates/next-core/src/dev_manifest.rs similarity index 100% rename from packages/next-swc/crates/next-core/src/manifest.rs rename to packages/next-swc/crates/next-core/src/dev_manifest.rs diff --git a/packages/next-swc/crates/next-core/src/emit.rs b/packages/next-swc/crates/next-core/src/emit.rs index b9a90adce1f7d..f4ba3435f8152 100644 --- a/packages/next-swc/crates/next-core/src/emit.rs +++ b/packages/next-swc/crates/next-core/src/emit.rs @@ -12,6 +12,9 @@ use turbopack_binding::turbopack::core::{ /// Emits all assets transitively reachable from the given chunks, that are /// inside the node root or the client root. +/// +/// Assets inside the given client root are rebased to the given client output +/// path. #[turbo_tasks::function] pub async fn emit_all_assets( assets: Vc, diff --git a/packages/next-swc/crates/next-core/src/lib.rs b/packages/next-swc/crates/next-core/src/lib.rs index 2641f1ee36b88..c118178539ad2 100644 --- a/packages/next-swc/crates/next-core/src/lib.rs +++ b/packages/next-swc/crates/next-core/src/lib.rs @@ -12,14 +12,14 @@ mod app_source; pub mod app_structure; mod babel; mod bootstrap; +pub mod dev_manifest; mod embed_js; mod emit; pub mod env; mod fallback; pub mod loader_tree; -pub mod manifest; pub mod mode; -pub(crate) mod next_app; +pub mod next_app; mod next_build; pub mod next_client; pub mod next_client_chunks; @@ -31,6 +31,7 @@ mod next_edge; mod next_font; pub mod next_image; mod next_import_map; +pub mod next_manifests; mod next_route_matcher; pub mod next_server; pub mod next_server_component; @@ -49,7 +50,6 @@ mod web_entry_source; pub use app_source::create_app_source; pub use emit::emit_all_assets; -pub use next_app::unsupported_dynamic_metadata_issue::UnsupportedDynamicMetadataIssue; pub use page_loader::create_page_loader_entry_module; pub use page_source::create_page_source; pub use turbopack_binding::{turbopack::node::source_map, *}; diff --git a/packages/next-swc/crates/next-build/src/next_app/app_client_reference.rs b/packages/next-swc/crates/next-core/src/next_app/app_client_references_chunks.rs similarity index 66% rename from packages/next-swc/crates/next-build/src/next_app/app_client_reference.rs rename to packages/next-swc/crates/next-core/src/next_app/app_client_references_chunks.rs index 5e1cb22aaebd7..554b11e92e400 100644 --- a/packages/next-swc/crates/next-build/src/next_app/app_client_reference.rs +++ b/packages/next-swc/crates/next-core/src/next_app/app_client_references_chunks.rs @@ -1,33 +1,44 @@ -use std::collections::HashSet; - use anyhow::Result; use indexmap::IndexMap; -use next_core::{ - next_client_reference::ClientReferenceType, - {self}, -}; -use turbo_tasks::{TryJoinIterExt, Vc}; +use serde::{Deserialize, Serialize}; +use turbo_tasks::{debug::ValueDebugFormat, trace::TraceRawVcs, TryJoinIterExt, Vc}; use turbopack_binding::turbopack::{ build::BuildChunkingContext, core::{ chunk::{ChunkableModule, ChunkingContext}, - output::{OutputAsset, OutputAssets}, + output::OutputAssets, }, ecmascript::chunk::EcmascriptChunkingContext, }; -/// Computes all client references chunks, and adds them to the relevant -/// manifests. +use crate::next_client_reference::{ClientReferenceType, ClientReferenceTypes}; + +/// Contains the chunks corresponding to a client reference. +#[derive( + Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, +)] +pub struct ClientReferenceChunks { + /// Chunks to be loaded on the client. + pub client_chunks: Vc, + /// Chunks to be loaded on the server for SSR. + pub ssr_chunks: Vc, +} + +#[turbo_tasks::value(transparent)] +pub struct ClientReferencesChunks(IndexMap); + +/// Computes all client references chunks. /// /// This returns a map from client reference type to the chunks that reference /// type needs to load. -pub async fn compute_app_client_references_chunks( - app_client_reference_types: &HashSet, +#[turbo_tasks::function] +pub async fn get_app_client_references_chunks( + app_client_reference_types: Vc, client_chunking_context: Vc>, ssr_chunking_context: Vc, - all_chunks: &mut Vec>>, -) -> Result> { +) -> Result> { let app_client_references_chunks: IndexMap<_, _> = app_client_reference_types + .await? .iter() .map(|client_reference_ty| async move { Ok(( @@ -64,28 +75,5 @@ pub async fn compute_app_client_references_chunks( .into_iter() .collect(); - for (app_client_reference_ty, app_client_reference_chunks) in &app_client_references_chunks { - match app_client_reference_ty { - ClientReferenceType::EcmascriptClientReference(_) => { - let client_chunks = &app_client_reference_chunks.client_chunks.await?; - let ssr_chunks = &app_client_reference_chunks.ssr_chunks.await?; - all_chunks.extend(client_chunks.iter().copied()); - all_chunks.extend(ssr_chunks.iter().copied()); - } - ClientReferenceType::CssClientReference(_) => { - let client_chunks = &app_client_reference_chunks.client_chunks.await?; - all_chunks.extend(client_chunks.iter().copied()); - } - } - } - - Ok(app_client_references_chunks) -} - -/// Contains the chunks corresponding to a client reference. -pub struct ClientReferenceChunks { - /// Chunks to be loaded on the client. - pub client_chunks: Vc, - /// Chunks to be loaded on the server for SSR. - pub ssr_chunks: Vc, + Ok(Vc::cell(app_client_references_chunks)) } diff --git a/packages/next-swc/crates/next-core/src/next_app/app_client_shared_chunks.rs b/packages/next-swc/crates/next-core/src/next_app/app_client_shared_chunks.rs new file mode 100644 index 0000000000000..cedcd7a506a6d --- /dev/null +++ b/packages/next-swc/crates/next-core/src/next_app/app_client_shared_chunks.rs @@ -0,0 +1,55 @@ +use anyhow::Result; +use turbo_tasks::{TryJoinIterExt, Value, Vc}; +use turbopack_binding::turbopack::{ + core::{ + chunk::{availability_info::AvailabilityInfo, ChunkingContext, EvaluatableAssets}, + output::OutputAssets, + }, + ecmascript::chunk::{EcmascriptChunk, EcmascriptChunkPlaceable, EcmascriptChunkingContext}, +}; + +#[turbo_tasks::function] +pub async fn get_app_shared_client_chunk( + app_client_runtime_entries: Vc, + client_chunking_context: Vc>, +) -> Result> { + let client_runtime_entries: Vec<_> = app_client_runtime_entries + .await? + .iter() + .map(|entry| async move { + Ok(Vc::try_resolve_sidecast::>(*entry).await?) + }) + .try_join() + .await? + .into_iter() + .flatten() + .collect(); + + Ok(EcmascriptChunk::new_normalized( + client_chunking_context, + // TODO(alexkirsz) Should this accept Evaluatable instead? + Vc::cell(client_runtime_entries), + None, + Value::new(AvailabilityInfo::Untracked), + )) +} + +#[turbo_tasks::function] +pub async fn get_app_client_shared_chunks( + app_client_runtime_entries: Vc, + client_chunking_context: Vc>, +) -> Result> { + if app_client_runtime_entries.await?.is_empty() { + return Ok(OutputAssets::empty()); + } + + let app_client_shared_chunk = + get_app_shared_client_chunk(app_client_runtime_entries, client_chunking_context); + + let app_client_shared_chunks = client_chunking_context.evaluated_chunk_group( + Vc::upcast(app_client_shared_chunk), + app_client_runtime_entries, + ); + + Ok(app_client_shared_chunks) +} diff --git a/packages/next-swc/crates/next-core/src/next_app/app_entry.rs b/packages/next-swc/crates/next-core/src/next_app/app_entry.rs new file mode 100644 index 0000000000000..341e6c3bed0bf --- /dev/null +++ b/packages/next-swc/crates/next-core/src/next_app/app_entry.rs @@ -0,0 +1,14 @@ +use turbo_tasks::Vc; +use turbopack_binding::turbopack::ecmascript::chunk::EcmascriptChunkPlaceable; + +/// The entry module asset for a Next.js app route or page. +#[turbo_tasks::value(shared)] +pub struct AppEntry { + /// The pathname of the route or page. + pub pathname: String, + /// The original Next.js name of the route or page. This is used instead of + /// the pathname to refer to this entry. + pub original_name: String, + /// The RSC module asset for the route or page. + pub rsc_entry: Vc>, +} diff --git a/packages/next-swc/crates/next-build/src/next_app/app_route_favicon_entry.rs b/packages/next-swc/crates/next-core/src/next_app/app_favicon_entry.rs similarity index 91% rename from packages/next-swc/crates/next-build/src/next_app/app_route_favicon_entry.rs rename to packages/next-swc/crates/next-core/src/next_app/app_favicon_entry.rs index fa789f94fcc69..6295673e15e1e 100644 --- a/packages/next-swc/crates/next-build/src/next_app/app_route_favicon_entry.rs +++ b/packages/next-swc/crates/next-core/src/next_app/app_favicon_entry.rs @@ -3,7 +3,6 @@ use std::io::Write; use anyhow::{bail, Result}; use base64::{display::Base64Display, engine::general_purpose::STANDARD}; use indoc::writedoc; -use next_core::app_structure::MetadataItem; use turbo_tasks::{ValueToString, Vc}; use turbopack_binding::{ turbo::tasks_fs::{rope::RopeBuilder, File, FileContent, FileSystemPath}, @@ -14,10 +13,12 @@ use turbopack_binding::{ }, }; -use super::{app_entries::AppEntry, app_route_entry::get_app_route_entry}; +use super::app_route_entry::get_app_route_entry; +use crate::{app_structure::MetadataItem, next_app::AppEntry}; /// Computes the entry for a Next.js favicon file. -pub(super) async fn get_app_route_favicon_entry( +#[turbo_tasks::function] +pub async fn get_app_route_favicon_entry( rsc_context: Vc, favicon: MetadataItem, project_root: Vc, @@ -77,12 +78,11 @@ pub(super) async fn get_app_route_favicon_entry( // TODO(alexkirsz) Figure out how to name this virtual source. VirtualSource::new(project_root.join("todo.tsx".to_string()), AssetContent::file(file.into())); - get_app_route_entry( + Ok(get_app_route_entry( rsc_context, Vc::upcast(source), // TODO(alexkirsz) Get this from the metadata? - "/favicon.ico", + "/favicon.ico".to_string(), project_root, - ) - .await + )) } diff --git a/packages/next-swc/crates/next-build/src/next_app/app_page_entry.rs b/packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs similarity index 93% rename from packages/next-swc/crates/next-build/src/next_app/app_page_entry.rs rename to packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs index 9f9745226cf1a..d2ffdaf1595a2 100644 --- a/packages/next-swc/crates/next-build/src/next_app/app_page_entry.rs +++ b/packages/next-swc/crates/next-core/src/next_app/app_page_entry.rs @@ -2,13 +2,6 @@ use std::io::Write; use anyhow::{bail, Result}; use indoc::writedoc; -use next_core::{ - app_structure::LoaderTree, - loader_tree::{LoaderTreeModule, ServerComponentTransition}, - mode::NextMode, - next_server_component::NextServerComponentTransition, - UnsupportedDynamicMetadataIssue, -}; use turbo_tasks::{TryJoinIterExt, Value, ValueToString, Vc}; use turbopack_binding::{ turbo::tasks_fs::{rope::RopeBuilder, File, FileSystemPath}, @@ -22,14 +15,22 @@ use turbopack_binding::{ }, }; -use super::app_entries::AppEntry; +use super::app_entry::AppEntry; +use crate::{ + app_structure::LoaderTree, + loader_tree::{LoaderTreeModule, ServerComponentTransition}, + mode::NextMode, + next_app::UnsupportedDynamicMetadataIssue, + next_server_component::NextServerComponentTransition, +}; /// Computes the entry for a Next.js app page. -pub(super) async fn get_app_page_entry( +#[turbo_tasks::function] +pub async fn get_app_page_entry( context: Vc, loader_tree: Vc, app_dir: Vc, - pathname: &str, + pathname: String, project_root: Vc, ) -> Result> { let server_component_transition = Vc::upcast(NextServerComponentTransition::new()); @@ -70,7 +71,7 @@ pub(super) async fn get_app_page_entry( // NOTE(alexkirsz) Keep in sync with // next.js/packages/next/src/build/webpack/loaders/next-app-loader.ts // TODO(alexkirsz) Support custom global error. - let original_name = get_original_page_name(pathname); + let original_name = get_original_page_name(&pathname); writedoc!( result, diff --git a/packages/next-swc/crates/next-build/src/next_app/app_route_entry.rs b/packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs similarity index 96% rename from packages/next-swc/crates/next-build/src/next_app/app_route_entry.rs rename to packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs index 459bab0a35062..7c1d96a9c1234 100644 --- a/packages/next-swc/crates/next-build/src/next_app/app_route_entry.rs +++ b/packages/next-swc/crates/next-core/src/next_app/app_route_entry.rs @@ -20,19 +20,20 @@ use turbopack_binding::{ }, }; -use super::app_entries::AppEntry; +use crate::next_app::AppEntry; /// Computes the entry for a Next.js app route. -pub(super) async fn get_app_route_entry( +#[turbo_tasks::function] +pub async fn get_app_route_entry( rsc_context: Vc, source: Vc>, - pathname: &str, + pathname: String, project_root: Vc, ) -> Result> { let mut result = RopeBuilder::default(); let kind = "app-route"; - let original_name = get_original_route_name(pathname); + let original_name = get_original_route_name(&pathname); let path = source.ident().path(); let options = AppRouteRouteModuleOptions { diff --git a/packages/next-swc/crates/next-core/src/next_app/mod.rs b/packages/next-swc/crates/next-core/src/next_app/mod.rs index 34ff1938e0335..7f40ca34fb94c 100644 --- a/packages/next-swc/crates/next-core/src/next_app/mod.rs +++ b/packages/next-swc/crates/next-core/src/next_app/mod.rs @@ -1 +1,17 @@ +pub(crate) mod app_client_references_chunks; +pub(crate) mod app_client_shared_chunks; +pub(crate) mod app_entry; +pub(crate) mod app_favicon_entry; +pub(crate) mod app_page_entry; +pub(crate) mod app_route_entry; pub(crate) mod unsupported_dynamic_metadata_issue; + +pub use app_client_references_chunks::{ + get_app_client_references_chunks, ClientReferenceChunks, ClientReferencesChunks, +}; +pub use app_client_shared_chunks::get_app_client_shared_chunks; +pub use app_entry::AppEntry; +pub use app_favicon_entry::get_app_route_favicon_entry; +pub use app_page_entry::get_app_page_entry; +pub use app_route_entry::get_app_route_entry; +pub use unsupported_dynamic_metadata_issue::UnsupportedDynamicMetadataIssue; diff --git a/packages/next-swc/crates/next-core/src/next_client_reference/mod.rs b/packages/next-swc/crates/next-core/src/next_client_reference/mod.rs index ea5e594bc2fe9..4f0d1bea8447b 100644 --- a/packages/next-swc/crates/next-core/src/next_client_reference/mod.rs +++ b/packages/next-swc/crates/next-core/src/next_client_reference/mod.rs @@ -7,4 +7,7 @@ pub use ecmascript_client_reference::{ ecmascript_client_reference_module::EcmascriptClientReferenceModule, ecmascript_client_reference_transition::NextEcmascriptClientReferenceTransition, }; -pub use visit_client_reference::{ClientReference, ClientReferenceType, ClientReferencesByEntry}; +pub use visit_client_reference::{ + ClientReference, ClientReferenceGraph, ClientReferenceType, ClientReferenceTypes, + ClientReferences, +}; diff --git a/packages/next-swc/crates/next-core/src/next_client_reference/visit_client_reference.rs b/packages/next-swc/crates/next-core/src/next_client_reference/visit_client_reference.rs index 725b1f149c81b..49a976eb3882a 100644 --- a/packages/next-swc/crates/next-core/src/next_client_reference/visit_client_reference.rs +++ b/packages/next-swc/crates/next-core/src/next_client_reference/visit_client_reference.rs @@ -1,7 +1,7 @@ use std::future::Future; use anyhow::Result; -use indexmap::IndexMap; +use indexmap::IndexSet; use serde::{Deserialize, Serialize}; use turbo_tasks::{ debug::ValueDebugFormat, @@ -47,12 +47,20 @@ pub enum ClientReferenceType { } #[turbo_tasks::value(transparent)] -pub struct ClientReferencesByEntry(IndexMap>, Vec>); +pub struct ClientReferences(Vec); + +#[turbo_tasks::value(transparent)] +pub struct ClientReferenceTypes(IndexSet); + +#[turbo_tasks::value(transparent)] +pub struct ClientReferenceGraph { + graph: AdjacencyMap, +} #[turbo_tasks::value_impl] -impl ClientReferencesByEntry { +impl ClientReferenceGraph { #[turbo_tasks::function] - pub async fn new(entries: Vc) -> Result> { + pub async fn new(entries: Vc) -> Result> { let entries = entries.await?; let graph = AdjacencyMap::new() @@ -72,42 +80,69 @@ impl ClientReferencesByEntry { .completed()? .into_inner(); - let client_references = entries - .iter() - .copied() - .map(|entry| { - let mut entry_client_references = vec![]; - for node in graph.reverse_topological_from_node(&VisitClientReferenceNode { - server_component: None, - ty: VisitClientReferenceNodeType::Internal(entry), - }) { - match &node.ty { - VisitClientReferenceNodeType::Internal(_asset) => { - // No-op. These nodes are only useful during graph - // traversal. - } - VisitClientReferenceNodeType::ClientReference(client_reference) => { - entry_client_references.push(*client_reference); - } - } + Ok(ClientReferenceGraph { graph }.cell()) + } + + #[turbo_tasks::function] + pub async fn types(self: Vc) -> Result> { + let this = self.await?; + let mut client_reference_types = IndexSet::new(); + + for node in this.graph.reverse_topological() { + match &node.ty { + VisitClientReferenceNodeType::Internal(_asset) => { + // No-op. These nodes are only useful during graph + // traversal. } - (entry, entry_client_references) + VisitClientReferenceNodeType::ClientReference(client_reference) => { + client_reference_types.insert(*client_reference.ty()); + } + } + } + + Ok(Vc::cell(client_reference_types)) + } + + #[turbo_tasks::function] + pub async fn entry(self: Vc, entry: Vc>) -> Result> { + let this = self.await?; + let mut entry_client_references = vec![]; + + for node in this + .graph + .reverse_topological_from_node(&VisitClientReferenceNode { + server_component: None, + ty: VisitClientReferenceNodeType::Internal(entry), }) - .collect(); + { + match &node.ty { + VisitClientReferenceNodeType::Internal(_asset) => { + // No-op. These nodes are only useful during graph + // traversal. + } + VisitClientReferenceNodeType::ClientReference(client_reference) => { + entry_client_references.push(*client_reference); + } + } + } - Ok(Vc::cell(client_references)) + Ok(Vc::cell(entry_client_references)) } } struct VisitClientReference; -#[derive(Clone, Eq, PartialEq, Hash)] +#[derive( + Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Debug, ValueDebugFormat, TraceRawVcs, +)] struct VisitClientReferenceNode { server_component: Option>, ty: VisitClientReferenceNodeType, } -#[derive(Clone, Eq, PartialEq, Hash)] +#[derive( + Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Debug, ValueDebugFormat, TraceRawVcs, +)] enum VisitClientReferenceNodeType { ClientReference(ClientReference), Internal(Vc>), diff --git a/packages/next-swc/crates/next-core/src/next_manifests/client_reference_manifest.rs b/packages/next-swc/crates/next-core/src/next_manifests/client_reference_manifest.rs new file mode 100644 index 0000000000000..93037b2bfc62a --- /dev/null +++ b/packages/next-swc/crates/next-core/src/next_manifests/client_reference_manifest.rs @@ -0,0 +1,217 @@ +use anyhow::{Context, Result}; +use indoc::formatdoc; +use turbo_tasks::{TryJoinIterExt, ValueToString, Vc}; +use turbo_tasks_fs::{File, FileSystemPath}; +use turbopack_binding::turbopack::{ + core::{ + asset::{Asset, AssetContent}, + chunk::ModuleId as TurbopackModuleId, + output::OutputAsset, + raw_output::RawOutput, + virtual_source::VirtualSource, + }, + ecmascript::{ + chunk::{EcmascriptChunkItemExt, EcmascriptChunkPlaceable, EcmascriptChunkingContext}, + utils::StringifyJs, + }, +}; + +use super::{ClientReferenceManifest, ManifestNode, ManifestNodeEntry, ModuleId}; +use crate::{ + next_app::ClientReferencesChunks, + next_client_reference::{ClientReferenceType, ClientReferences}, +}; + +#[turbo_tasks::value_impl] +impl ClientReferenceManifest { + #[turbo_tasks::function] + pub async fn build_output( + node_root: Vc, + client_relative_path: Vc, + entry_name: String, + client_references: Vc, + client_references_chunks: Vc, + client_chunking_context: Vc>, + ssr_chunking_context: Vc>, + ) -> Result>> { + let mut entry_manifest: ClientReferenceManifest = Default::default(); + let client_references_chunks = client_references_chunks.await?; + let client_relative_path = client_relative_path.await?; + let node_root_ref = node_root.await?; + + for app_client_reference in client_references.await?.iter() { + let app_client_reference_ty = app_client_reference.ty(); + + let app_client_reference_chunks = client_references_chunks + .get(app_client_reference.ty()) + .context("client reference chunks not found")?; + + let client_reference_chunks = client_references_chunks + .get(app_client_reference_ty) + .context("client reference chunks not found")?; + let client_chunks = &client_reference_chunks.client_chunks.await?; + + let client_chunks_paths = client_chunks + .iter() + .map(|chunk| chunk.ident().path()) + .try_join() + .await?; + + if let Some(server_component) = app_client_reference.server_component() { + let server_component_name = server_component + .server_path() + .with_extension("".to_string()) + .to_string() + .await?; + + let entry_css_files = entry_manifest + .entry_css_files + .entry(server_component_name.clone_value()) + .or_insert_with(Default::default); + + match app_client_reference_ty { + ClientReferenceType::CssClientReference(_) => { + entry_css_files.extend( + client_chunks_paths + .iter() + .filter_map(|chunk_path| { + client_relative_path.get_path_to(chunk_path) + }) + .map(ToString::to_string), + ); + } + + ClientReferenceType::EcmascriptClientReference(_) => { + entry_css_files.extend( + client_chunks_paths + .iter() + .filter_map(|chunk_path| { + if chunk_path.extension_ref() == Some("css") { + client_relative_path.get_path_to(chunk_path) + } else { + None + } + }) + .map(ToString::to_string), + ); + } + } + } + + match app_client_reference_ty { + ClientReferenceType::CssClientReference(_) => {} + + ClientReferenceType::EcmascriptClientReference(ecmascript_client_reference) => { + let client_chunks = &app_client_reference_chunks.client_chunks.await?; + let ssr_chunks = &app_client_reference_chunks.ssr_chunks.await?; + + let ecmascript_client_reference = ecmascript_client_reference.await?; + + let client_module_id = ecmascript_client_reference + .client_module + .as_chunk_item(client_chunking_context) + .id() + .await?; + let ssr_module_id = ecmascript_client_reference + .ssr_module + .as_chunk_item(ssr_chunking_context) + .id() + .await?; + + let server_path = ecmascript_client_reference + .server_ident + .path() + .to_string() + .await?; + + let client_chunks_paths = client_chunks + .iter() + .map(|chunk| chunk.ident().path()) + .try_join() + .await?; + let client_chunks_paths: Vec = client_chunks_paths + .iter() + .filter_map(|chunk_path| client_relative_path.get_path_to(chunk_path)) + .map(ToString::to_string) + .collect::>(); + + let ssr_chunks_paths = ssr_chunks + .iter() + .map(|chunk| chunk.ident().path()) + .try_join() + .await?; + let ssr_chunks_paths = ssr_chunks_paths + .iter() + .filter_map(|chunk_path| node_root_ref.get_path_to(chunk_path)) + .map(ToString::to_string) + .collect::>(); + + let mut ssr_manifest_node = ManifestNode::default(); + + entry_manifest.client_modules.module_exports.insert( + get_client_reference_module_key(&server_path, "*"), + ManifestNodeEntry { + name: "*".to_string(), + id: (&*client_module_id).into(), + chunks: client_chunks_paths.clone(), + // TODO(WEB-434) + r#async: false, + }, + ); + + ssr_manifest_node.module_exports.insert( + "*".to_string(), + ManifestNodeEntry { + name: "*".to_string(), + id: (&*ssr_module_id).into(), + chunks: ssr_chunks_paths.clone(), + // TODO(WEB-434) + r#async: false, + }, + ); + + entry_manifest + .ssr_module_mapping + .insert((&*client_module_id).into(), ssr_manifest_node); + } + } + } + + let client_reference_manifest_json = serde_json::to_string(&entry_manifest).unwrap(); + + Ok(Vc::upcast(RawOutput::new(Vc::upcast(VirtualSource::new( + node_root.join(format!( + "server/app/{entry_name}_client-reference-manifest.js", + )), + AssetContent::file( + File::from(formatdoc! { + r#" + globalThis.__RSC_MANIFEST = globalThis.__RSC_MANIFEST || {{}}; + globalThis.__RSC_MANIFEST[{entry_name}] = {manifest} + "#, + entry_name = StringifyJs(&entry_name), + manifest = StringifyJs(&client_reference_manifest_json) + }) + .into(), + ), + ))))) + } +} + +impl From<&TurbopackModuleId> for ModuleId { + fn from(module_id: &TurbopackModuleId) -> Self { + match module_id { + TurbopackModuleId::String(string) => ModuleId::String(string.clone()), + TurbopackModuleId::Number(number) => ModuleId::Number(*number as _), + } + } +} + +/// See next.js/packages/next/src/lib/client-reference.ts +pub fn get_client_reference_module_key(server_path: &str, export_name: &str) -> String { + if export_name == "*" { + server_path.to_string() + } else { + format!("{}#{}", server_path, export_name) + } +} diff --git a/packages/next-swc/crates/next-build/src/manifests.rs b/packages/next-swc/crates/next-core/src/next_manifests/mod.rs similarity index 98% rename from packages/next-swc/crates/next-build/src/manifests.rs rename to packages/next-swc/crates/next-core/src/next_manifests/mod.rs index 329cd4b518cb6..74559235f0e24 100644 --- a/packages/next-swc/crates/next-build/src/manifests.rs +++ b/packages/next-swc/crates/next-core/src/next_manifests/mod.rs @@ -1,10 +1,13 @@ //! Type definitions for the Next.js manifest formats. +pub(crate) mod client_reference_manifest; + use std::collections::HashMap; -use next_core::next_config::Rewrites; use serde::Serialize; +use crate::next_config::Rewrites; + #[derive(Serialize, Default, Debug)] pub struct PagesManifest { #[serde(flatten)] diff --git a/packages/next-swc/crates/next-dev/src/lib.rs b/packages/next-swc/crates/next-dev/src/lib.rs index 259ff7dcf3d7d..9161ad2120606 100644 --- a/packages/next-swc/crates/next-dev/src/lib.rs +++ b/packages/next-swc/crates/next-dev/src/lib.rs @@ -24,7 +24,7 @@ use indexmap::IndexMap; use next_core::{ app_structure::find_app_dir_if_enabled, create_app_source, create_page_source, create_web_entry_source, - manifest::DevManifestContentSource, + dev_manifest::DevManifestContentSource, mode::NextMode, next_client::{get_client_chunking_context, get_client_compile_time_info}, next_config::{load_next_config, load_rewrites}, diff --git a/packages/next/src/cli/next-dev.ts b/packages/next/src/cli/next-dev.ts index 46e045ec4eb13..a0022146e35cf 100644 --- a/packages/next/src/cli/next-dev.ts +++ b/packages/next/src/cli/next-dev.ts @@ -265,6 +265,12 @@ const nextDev: CliCommand = async (argv) => { Log.info(written) break } + case 'app-page': { + Log.info(`writing ${pathname} to disk`) + const written = await route.rscEndpoint.writeToDisk() + Log.info(written) + break + } default: Log.info(`skipping ${pathname} (${route.type})`) break diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23ca3a5e37891..3416fc7e986de 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -992,8 +992,8 @@ importers: '@types/react': 18.2.7 '@types/react-dom': 18.2.4 '@vercel/ncc': ^0.36.0 - '@vercel/turbopack-ecmascript-runtime': https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230716.2 - '@vercel/turbopack-node': https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230716.2 + '@vercel/turbopack-ecmascript-runtime': https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230717.2 + '@vercel/turbopack-node': https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230717.2 anser: ^2.1.1 css.escape: ^1.5.1 find-up: ^6.3.0 @@ -1005,8 +1005,8 @@ importers: stacktrace-parser: ^0.1.10 strip-ansi: ^7.0.1 dependencies: - '@vercel/turbopack-ecmascript-runtime': '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230716.2_react-refresh@0.12.0' - '@vercel/turbopack-node': '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230716.2' + '@vercel/turbopack-ecmascript-runtime': '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230717.2_react-refresh@0.12.0' + '@vercel/turbopack-node': '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230717.2' anser: 2.1.1 css.escape: 1.5.1 next: link:../../../../next @@ -25498,9 +25498,9 @@ packages: /zwitch/2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230716.2_react-refresh@0.12.0': - resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230716.2} - id: '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230716.2' + '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230717.2_react-refresh@0.12.0': + resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230717.2} + id: '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230717.2' name: '@vercel/turbopack-ecmascript-runtime' version: 0.0.0 dependencies: @@ -25511,8 +25511,8 @@ packages: - webpack dev: false - '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230716.2': - resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230716.2} + '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230717.2': + resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230717.2} name: '@vercel/turbopack-node' version: 0.0.0 dependencies: