add edge rendering for app dir for Turbopack (#51666)
relanding #50830 and #51631 ### Turbopack Changes * https://github.com/vercel/turbo/pull/5254 <!-- OJ Kwon - ci(workflow): upload daily next.js test trace --> * https://github.com/vercel/turbo/pull/5363 <!-- Tobias Koppers - disable default features for turbopack-ecmascript-plugins -->
This commit is contained in:
parent
f1624b65b4
commit
5abeb99b49
41 changed files with 1430 additions and 782 deletions
72
Cargo.lock
generated
72
Cargo.lock
generated
|
@ -400,7 +400,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "auto-hash-map"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -3463,7 +3463,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "node-file-trace"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
|
@ -6412,6 +6412,7 @@ dependencies = [
|
|||
"swc_common",
|
||||
"swc_ecma_ast",
|
||||
"swc_plugin_proxy",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"wasmer",
|
||||
"wasmer-cache",
|
||||
|
@ -7103,7 +7104,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto-hash-map",
|
||||
|
@ -7134,7 +7135,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-build"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo-lock",
|
||||
|
@ -7146,7 +7147,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-bytes"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
|
@ -7161,7 +7162,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-env"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dotenvy",
|
||||
|
@ -7175,7 +7176,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-fetch"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
|
@ -7192,7 +7193,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-fs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto-hash-map",
|
||||
|
@ -7222,7 +7223,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-hash"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"base16",
|
||||
"hex",
|
||||
|
@ -7234,7 +7235,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
|
@ -7248,7 +7249,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-macros-shared"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -7258,7 +7259,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-malloc"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"mimalloc",
|
||||
]
|
||||
|
@ -7266,7 +7267,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-memory"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto-hash-map",
|
||||
|
@ -7289,7 +7290,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-testing"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto-hash-map",
|
||||
|
@ -7301,7 +7302,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
|
@ -7331,7 +7332,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-bench"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chromiumoxide",
|
||||
|
@ -7361,7 +7362,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-binding"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"auto-hash-map",
|
||||
"mdxjs",
|
||||
|
@ -7403,7 +7404,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-build"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230621.2#ee6683e43e31a470e13f0bc16dbaa375face74b5"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
|
@ -7423,7 +7424,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-cli-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.11",
|
||||
|
@ -7447,7 +7448,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-core"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -7475,7 +7476,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-create-test-app"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.11",
|
||||
|
@ -7488,7 +7489,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-css"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -7510,7 +7511,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-dev"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
|
@ -7534,7 +7535,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-dev-server"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
|
@ -7569,7 +7570,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-ecmascript"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -7602,7 +7603,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-ecmascript-plugins"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -7625,7 +7626,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-ecmascript-runtime"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indoc",
|
||||
|
@ -7642,7 +7643,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-env"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
|
@ -7658,7 +7659,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-image"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.0",
|
||||
|
@ -7678,7 +7679,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-json"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
|
@ -7693,7 +7694,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-mdx"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mdxjs",
|
||||
|
@ -7708,7 +7709,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-node"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
|
@ -7743,7 +7744,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-static"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
|
@ -7759,7 +7760,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-swc-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"swc_core",
|
||||
"turbo-tasks",
|
||||
|
@ -7770,7 +7771,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-test-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230615.1#1ff1956dc18ff1805b2ac87f21f79e1abea75fc8"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230622.2#08aba99a2796b37324cabf18b6ea9eff886d93b4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"once_cell",
|
||||
|
@ -8450,6 +8451,7 @@ dependencies = [
|
|||
"pin-project",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_yaml 0.8.26",
|
||||
|
|
|
@ -42,11 +42,11 @@ swc_core = { version = "0.76.46" }
|
|||
testing = { version = "0.33.13" }
|
||||
|
||||
# Turbo crates
|
||||
turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230615.1" }
|
||||
turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230622.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-230615.1" }
|
||||
turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230622.2" }
|
||||
# [TODO]: need to refactor embed_directory! macro usage in next-core
|
||||
turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230615.1" }
|
||||
turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230622.2" }
|
||||
|
||||
# General Deps
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
"check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/turbopack-ecmascript-runtime": "https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230615.1",
|
||||
"@vercel/turbopack-node": "https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-node/js?turbopack-230615.1",
|
||||
"@vercel/turbopack-ecmascript-runtime": "https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230622.2",
|
||||
"@vercel/turbopack-node": "https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-node/js?turbopack-230622.2",
|
||||
"anser": "^2.1.1",
|
||||
"css.escape": "^1.5.1",
|
||||
"next": "*",
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
// IPC need to be the first import to allow it to catch errors happening during
|
||||
// the other imports
|
||||
import startOperationStreamHandler from '../internal/operation-stream'
|
||||
|
||||
import { join } from 'path'
|
||||
import { parse as parseUrl } from 'node:url'
|
||||
|
||||
import { runEdgeFunction } from '../internal/edge'
|
||||
import { headersFromEntries, initProxiedHeaders } from '../internal/headers'
|
||||
import { NodeNextRequest } from 'next/dist/server/base-http/node'
|
||||
|
||||
import type { IncomingMessage } from 'node:http'
|
||||
import type { RenderData } from 'types/turbopack'
|
||||
|
||||
import chunkGroup from 'INNER_EDGE_CHUNK_GROUP'
|
||||
import { attachRequestMeta } from '../internal/next-request-helpers'
|
||||
import { Readable } from 'stream'
|
||||
|
||||
startOperationStreamHandler(async (renderData: RenderData, respond) => {
|
||||
const { response } = await runOperation(renderData)
|
||||
|
||||
if (response == null) {
|
||||
throw new Error('no html returned')
|
||||
}
|
||||
|
||||
const channel = respond({
|
||||
status: response.status,
|
||||
// @ts-expect-error Headers is iterable since node.js 18
|
||||
headers: [...response.headers],
|
||||
})
|
||||
|
||||
if (response.body) {
|
||||
const reader = response.body.getReader()
|
||||
for (;;) {
|
||||
let { done, value } = await reader.read()
|
||||
if (done) {
|
||||
break
|
||||
}
|
||||
channel.chunk(Buffer.from(value!))
|
||||
}
|
||||
}
|
||||
|
||||
channel.end()
|
||||
})
|
||||
|
||||
async function runOperation(renderData: RenderData) {
|
||||
const edgeInfo = {
|
||||
name: 'edge',
|
||||
paths: chunkGroup
|
||||
.filter((chunk) => chunk.endsWith('.js'))
|
||||
.map((chunk: string) => join(process.cwd(), '.next/server/app', chunk)),
|
||||
wasm: [],
|
||||
env: Object.keys(process.env),
|
||||
assets: [],
|
||||
}
|
||||
|
||||
const parsedUrl = parseUrl(renderData.originalUrl, true)
|
||||
const incoming = new Readable() as IncomingMessage
|
||||
incoming.push(null)
|
||||
incoming.url = renderData.originalUrl
|
||||
incoming.method = renderData.method
|
||||
incoming.headers = initProxiedHeaders(
|
||||
headersFromEntries(renderData.rawHeaders),
|
||||
renderData.data?.serverInfo
|
||||
)
|
||||
const req = new NodeNextRequest(incoming)
|
||||
attachRequestMeta(req, parsedUrl, req.headers.host!)
|
||||
|
||||
const res = await runEdgeFunction({
|
||||
edgeInfo,
|
||||
outputDir: 'edge-pages',
|
||||
req,
|
||||
query: renderData.rawQuery,
|
||||
params: renderData.params,
|
||||
path: renderData.path,
|
||||
onWarning(warning) {
|
||||
console.warn(warning)
|
||||
},
|
||||
})
|
||||
|
||||
return res as { response: Response }
|
||||
}
|
|
@ -1,22 +1,11 @@
|
|||
// Provided by the rust generate code
|
||||
type FileType =
|
||||
| 'layout'
|
||||
| 'template'
|
||||
| 'error'
|
||||
| 'loading'
|
||||
| 'not-found'
|
||||
| 'head'
|
||||
declare global {
|
||||
// an tree of all layouts and the page
|
||||
const LOADER_TREE: LoaderTree
|
||||
// array of chunks for the bootstrap script
|
||||
const BOOTSTRAP: string[]
|
||||
const IPC: Ipc<unknown, unknown>
|
||||
}
|
||||
// IPC need to be the first import to allow it to catch errors happening during
|
||||
// the other imports
|
||||
import startOperationStreamHandler from '../internal/operation-stream'
|
||||
|
||||
import '../polyfill/app-polyfills.ts'
|
||||
|
||||
import type { Ipc } from '@vercel/turbopack-node/ipc/index'
|
||||
import type { IncomingMessage } from 'node:http'
|
||||
import type { ClientReferenceManifest } from 'next/dist/build/webpack/plugins/flight-manifest-plugin'
|
||||
|
||||
import type { RenderData } from 'types/turbopack'
|
||||
import type { RenderOpts } from 'next/dist/server/app-render/types'
|
||||
|
||||
|
@ -25,211 +14,39 @@ import { RSC_VARY_HEADER } from 'next/dist/client/components/app-router-headers'
|
|||
import { headersFromEntries, initProxiedHeaders } from '../internal/headers'
|
||||
import { parse, ParsedUrlQuery } from 'node:querystring'
|
||||
import { PassThrough } from 'node:stream'
|
||||
;('TURBOPACK { transition: next-layout-entry; chunking-type: isolatedParallel }')
|
||||
// @ts-ignore
|
||||
import layoutEntry from './app/layout-entry'
|
||||
;('TURBOPACK { chunking-type: isolatedParallel }')
|
||||
import entry from 'APP_ENTRY'
|
||||
import BOOTSTRAP from 'APP_BOOTSTRAP'
|
||||
import { createServerResponse } from '../internal/http'
|
||||
import { createManifests, installRequireAndChunkLoad } from './app/manifest'
|
||||
|
||||
globalThis.__next_require__ = (data) => {
|
||||
const [, , ssr_id] = JSON.parse(data)
|
||||
return __turbopack_require__(ssr_id)
|
||||
}
|
||||
globalThis.__next_chunk_load__ = () => Promise.resolve()
|
||||
installRequireAndChunkLoad()
|
||||
|
||||
process.env.__NEXT_NEW_LINK_BEHAVIOR = 'true'
|
||||
|
||||
const ipc = IPC as Ipc<IpcIncomingMessage, IpcOutgoingMessage>
|
||||
|
||||
type IpcIncomingMessage = {
|
||||
type: 'headers'
|
||||
data: RenderData
|
||||
}
|
||||
|
||||
type IpcOutgoingMessage =
|
||||
| {
|
||||
type: 'headers'
|
||||
data: {
|
||||
status: number
|
||||
headers: [string, string][]
|
||||
}
|
||||
}
|
||||
| {
|
||||
type: 'bodyChunk'
|
||||
data: number[]
|
||||
}
|
||||
| {
|
||||
type: 'bodyEnd'
|
||||
}
|
||||
|
||||
const MIME_TEXT_HTML_UTF8 = 'text/html; charset=utf-8'
|
||||
|
||||
;(async () => {
|
||||
while (true) {
|
||||
const msg = await ipc.recv()
|
||||
startOperationStreamHandler(async (renderData: RenderData, respond) => {
|
||||
const result = await runOperation(renderData)
|
||||
|
||||
let renderData: RenderData
|
||||
switch (msg.type) {
|
||||
case 'headers': {
|
||||
renderData = msg.data
|
||||
break
|
||||
}
|
||||
default: {
|
||||
console.error('unexpected message type', msg.type)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
const result = await runOperation(renderData)
|
||||
|
||||
if (result == null) {
|
||||
throw new Error('no html returned')
|
||||
}
|
||||
|
||||
ipc.send({
|
||||
type: 'headers',
|
||||
data: {
|
||||
status: result.statusCode,
|
||||
headers: result.headers,
|
||||
},
|
||||
})
|
||||
|
||||
for await (const chunk of result.body) {
|
||||
ipc.send({
|
||||
type: 'bodyChunk',
|
||||
data: (chunk as Buffer).toJSON().data,
|
||||
})
|
||||
}
|
||||
|
||||
ipc.send({ type: 'bodyEnd' })
|
||||
if (result == null) {
|
||||
throw new Error('no html returned')
|
||||
}
|
||||
})().catch((err) => {
|
||||
ipc.sendError(err)
|
||||
|
||||
const channel = respond({
|
||||
status: result.statusCode,
|
||||
headers: result.headers,
|
||||
})
|
||||
|
||||
for await (const chunk of result.body) {
|
||||
channel.chunk(chunk as Buffer)
|
||||
}
|
||||
|
||||
channel.end()
|
||||
})
|
||||
|
||||
// TODO expose these types in next.js
|
||||
type ComponentModule = () => any
|
||||
type ModuleReference = [componentModule: ComponentModule, filePath: string]
|
||||
export type ComponentsType = {
|
||||
[componentKey in FileType]?: ModuleReference
|
||||
} & {
|
||||
page?: ModuleReference
|
||||
}
|
||||
type LoaderTree = [
|
||||
segment: string,
|
||||
parallelRoutes: { [parallelRouterKey: string]: LoaderTree },
|
||||
components: ComponentsType
|
||||
]
|
||||
|
||||
async function runOperation(renderData: RenderData) {
|
||||
const proxyMethodsForModule = (
|
||||
id: string
|
||||
): ProxyHandler<ClientReferenceManifest['ssrModuleMapping']> => {
|
||||
return {
|
||||
get(_target, prop: string) {
|
||||
return {
|
||||
id,
|
||||
chunks: JSON.parse(id)[1],
|
||||
name: prop,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const proxyMethodsNested = (
|
||||
type: 'ssrModuleMapping' | 'clientModules' | 'entryCSSFiles'
|
||||
): ProxyHandler<
|
||||
| ClientReferenceManifest['ssrModuleMapping']
|
||||
| ClientReferenceManifest['clientModules']
|
||||
| ClientReferenceManifest['entryCSSFiles']
|
||||
> => {
|
||||
return {
|
||||
get(_target, key: string) {
|
||||
if (type === 'ssrModuleMapping') {
|
||||
return new Proxy({}, proxyMethodsForModule(key as string))
|
||||
}
|
||||
if (type === 'clientModules') {
|
||||
// The key is a `${file}#${name}`, but `file` can contain `#` itself.
|
||||
// There are 2 possibilities:
|
||||
// "file#" => id = "file", name = ""
|
||||
// "file#foo" => id = "file", name = "foo"
|
||||
const pos = key.lastIndexOf('#')
|
||||
let id = key
|
||||
let name = ''
|
||||
if (pos !== -1) {
|
||||
id = key.slice(0, pos)
|
||||
name = key.slice(pos + 1)
|
||||
} else {
|
||||
throw new Error('keys need to be formatted as {file}#{name}')
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
chunks: JSON.parse(id)[1],
|
||||
}
|
||||
}
|
||||
if (type === 'entryCSSFiles') {
|
||||
const cssChunks = JSON.parse(key)
|
||||
// TODO(WEB-856) subscribe to changes
|
||||
return {
|
||||
modules: [],
|
||||
files: cssChunks.filter(filterAvailable).map(toPath),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const proxyMethods = (): ProxyHandler<ClientReferenceManifest> => {
|
||||
const clientModulesProxy = new Proxy(
|
||||
{},
|
||||
proxyMethodsNested('clientModules')
|
||||
)
|
||||
const ssrModuleMappingProxy = new Proxy(
|
||||
{},
|
||||
proxyMethodsNested('ssrModuleMapping')
|
||||
)
|
||||
const entryCSSFilesProxy = new Proxy(
|
||||
{},
|
||||
proxyMethodsNested('entryCSSFiles')
|
||||
)
|
||||
return {
|
||||
get(_target: any, prop: string) {
|
||||
if (prop === 'ssrModuleMapping') {
|
||||
return ssrModuleMappingProxy
|
||||
}
|
||||
if (prop === 'clientModules') {
|
||||
return clientModulesProxy
|
||||
}
|
||||
if (prop === 'entryCSSFiles') {
|
||||
return entryCSSFilesProxy
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
const availableModules = new Set()
|
||||
const toPath = (chunk: ChunkData) =>
|
||||
typeof chunk === 'string' ? chunk : chunk.path
|
||||
/// determines if a chunk is needed based on the current available modules
|
||||
const filterAvailable = (chunk: ChunkData) => {
|
||||
if (typeof chunk === 'string') {
|
||||
return true
|
||||
} else {
|
||||
let includedList = chunk.included || []
|
||||
if (includedList.length === 0) {
|
||||
return true
|
||||
}
|
||||
let needed = false
|
||||
for (const item of includedList) {
|
||||
if (!availableModules.has(item)) {
|
||||
availableModules.add(item)
|
||||
needed = true
|
||||
}
|
||||
}
|
||||
return needed
|
||||
}
|
||||
}
|
||||
const manifest: ClientReferenceManifest = new Proxy({} as any, proxyMethods())
|
||||
const { clientReferenceManifest } = createManifests()
|
||||
|
||||
const req: IncomingMessage = {
|
||||
url: renderData.originalUrl,
|
||||
|
@ -266,12 +83,14 @@ async function runOperation(renderData: RenderData) {
|
|||
ampFirstPages: [],
|
||||
},
|
||||
ComponentMod: {
|
||||
...layoutEntry,
|
||||
default: undefined,
|
||||
tree: LOADER_TREE,
|
||||
...entry,
|
||||
__next_app__: {
|
||||
require: __next_require__,
|
||||
loadChunk: __next_chunk_load__,
|
||||
},
|
||||
pages: ['page.js'],
|
||||
},
|
||||
clientReferenceManifest: manifest,
|
||||
clientReferenceManifest,
|
||||
runtime: 'nodejs',
|
||||
serverComponents: true,
|
||||
assetPrefix: '',
|
||||
|
@ -304,23 +123,3 @@ async function runOperation(renderData: RenderData) {
|
|||
body,
|
||||
}
|
||||
}
|
||||
|
||||
// This utility is based on https://github.com/zertosh/htmlescape
|
||||
// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE
|
||||
|
||||
const ESCAPE_LOOKUP = {
|
||||
'&': '\\u0026',
|
||||
'>': '\\u003e',
|
||||
'<': '\\u003c',
|
||||
'\u2028': '\\u2028',
|
||||
'\u2029': '\\u2029',
|
||||
}
|
||||
|
||||
const ESCAPE_REGEX = /[&><\u2028\u2029]/g
|
||||
|
||||
export function htmlEscapeJsonString(str: string) {
|
||||
return str.replace(
|
||||
ESCAPE_REGEX,
|
||||
(match) => ESCAPE_LOOKUP[match as keyof typeof ESCAPE_LOOKUP]
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
// This file is generated by app_source.rs
|
|
@ -0,0 +1,106 @@
|
|||
import { adapter } from 'next/dist/server/web/adapter'
|
||||
import { RSC_VARY_HEADER } from 'next/dist/client/components/app-router-headers'
|
||||
import { IncrementalCache } from 'next/dist/server/lib/incremental-cache'
|
||||
import { renderToHTMLOrFlight } from 'next/dist/server/app-render/app-render'
|
||||
;('TURBOPACK { chunking-type: isolatedParallel }')
|
||||
import entry from 'APP_ENTRY'
|
||||
import BOOTSTRAP from 'APP_BOOTSTRAP'
|
||||
import { createManifests, installRequireAndChunkLoad } from './manifest'
|
||||
import type { NextRequest, NextFetchEvent } from 'next/server'
|
||||
import type { RenderOpts } from 'next/dist/server/app-render/types'
|
||||
import type { ParsedUrlQuery } from 'querystring'
|
||||
|
||||
installRequireAndChunkLoad()
|
||||
|
||||
// avoid limiting stack traces to 10 lines
|
||||
Error.stackTraceLimit = 100
|
||||
|
||||
const { clientReferenceManifest } = createManifests()
|
||||
|
||||
const MIME_TEXT_HTML_UTF8 = 'text/html; charset=utf-8'
|
||||
|
||||
async function render(request: NextRequest, event: NextFetchEvent) {
|
||||
const renderOpt: Omit<
|
||||
RenderOpts,
|
||||
'App' | 'Document' | 'Component' | 'pathname'
|
||||
> & { params: ParsedUrlQuery } = {
|
||||
// TODO(WEB-1195) params
|
||||
params: {},
|
||||
supportsDynamicHTML: true,
|
||||
dev: true,
|
||||
buildId: 'development',
|
||||
buildManifest: {
|
||||
polyfillFiles: [],
|
||||
rootMainFiles: BOOTSTRAP.filter((path) => path.endsWith('.js')),
|
||||
devFiles: [],
|
||||
ampDevFiles: [],
|
||||
lowPriorityFiles: [],
|
||||
pages: {
|
||||
'/_app': [],
|
||||
},
|
||||
ampFirstPages: [],
|
||||
},
|
||||
ComponentMod: {
|
||||
...entry,
|
||||
__next_app__: {
|
||||
require: __next_require__,
|
||||
loadChunk: __next_chunk_load__,
|
||||
},
|
||||
pages: ['page.js'],
|
||||
},
|
||||
clientReferenceManifest,
|
||||
runtime: 'nodejs',
|
||||
serverComponents: true,
|
||||
assetPrefix: '',
|
||||
pageConfig: {},
|
||||
reactLoadableManifest: {},
|
||||
// TODO nextConfigOutput
|
||||
nextConfigOutput: undefined,
|
||||
}
|
||||
|
||||
const tranform = new TransformStream()
|
||||
const response = new Response(tranform.readable)
|
||||
|
||||
let { pathname, search: query } = new URL(request.url, 'next://')
|
||||
|
||||
const result = await renderToHTMLOrFlight(
|
||||
// @ts-expect-error - TODO renderToHTMLOrFlight types should accept web platform types
|
||||
request,
|
||||
response,
|
||||
pathname,
|
||||
// TODO(WEB-1195) query
|
||||
{},
|
||||
renderOpt as any as RenderOpts
|
||||
)
|
||||
|
||||
response.headers.append(
|
||||
'Content-Type',
|
||||
result.contentType || MIME_TEXT_HTML_UTF8
|
||||
)
|
||||
response.headers.append('Vary', RSC_VARY_HEADER)
|
||||
|
||||
const writer = tranform.writable.getWriter()
|
||||
result.pipe({
|
||||
write: (chunk: Uint8Array) => writer.write(chunk),
|
||||
end: () => writer.close(),
|
||||
destroy: (reason?: Error) => writer.abort(reason),
|
||||
})
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// adapter uses this to detect edge rendering
|
||||
self.__BUILD_MANIFEST = {}
|
||||
|
||||
// @ts-expect-error - exposed for edge support
|
||||
globalThis._ENTRIES = {
|
||||
middleware_edge: {
|
||||
default: function (opts: any) {
|
||||
return adapter({
|
||||
...opts,
|
||||
IncrementalCache,
|
||||
handler: render,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
|
@ -12,7 +12,7 @@ import {
|
|||
NodeNextResponse,
|
||||
} from 'next/dist/server/base-http/node'
|
||||
|
||||
import { runEdgeFunction } from '../../internal/edge'
|
||||
import { runEdgeFunction, updateResponse } from '../../internal/edge'
|
||||
import { attachRequestMeta } from '../../internal/next-request-helpers'
|
||||
|
||||
import chunkGroup from 'ROUTE_CHUNK_GROUP'
|
||||
|
@ -34,11 +34,10 @@ startHandler(async ({ request, response, query, params, path }) => {
|
|||
assets: [],
|
||||
}
|
||||
|
||||
await runEdgeFunction({
|
||||
const result = await runEdgeFunction({
|
||||
edgeInfo,
|
||||
outputDir: 'app',
|
||||
req,
|
||||
res,
|
||||
query,
|
||||
params,
|
||||
path,
|
||||
|
@ -46,4 +45,6 @@ startHandler(async ({ request, response, query, params, path }) => {
|
|||
console.warn(warning)
|
||||
},
|
||||
})
|
||||
|
||||
updateResponse(res, result)
|
||||
})
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
export { default as AppRouter } from 'next/dist/client/components/app-router'
|
||||
export { default as LayoutRouter } from 'next/dist/client/components/layout-router'
|
||||
export { default as RenderFromTemplateContext } from 'next/dist/client/components/render-from-template-context'
|
||||
export { default as GlobalError } from 'next/dist/client/components/error-boundary'
|
||||
|
||||
export { staticGenerationAsyncStorage } from 'next/dist/client/components/static-generation-async-storage'
|
||||
|
||||
export { requestAsyncStorage } from 'next/dist/client/components/request-async-storage'
|
||||
export { actionAsyncStorage } from 'next/dist/client/components/action-async-storage'
|
||||
|
||||
export { staticGenerationBailout } from 'next/dist/client/components/static-generation-bailout'
|
||||
export { default as StaticGenerationSearchParamsBailoutProvider } from 'next/dist/client/components/static-generation-searchparams-bailout-provider'
|
||||
export { createSearchParamsBailoutProxy } from 'next/dist/client/components/searchparams-bailout-proxy'
|
||||
|
||||
import * as serverHooks from 'next/dist/client/components/hooks-server-context'
|
||||
export { serverHooks }
|
||||
export {
|
||||
renderToReadableStream,
|
||||
decodeReply,
|
||||
} from 'next/dist/compiled/react-server-dom-webpack/server.edge'
|
||||
export {
|
||||
preloadStyle,
|
||||
preloadFont,
|
||||
preconnect,
|
||||
} from 'next/dist/server/app-render/rsc/preloads'
|
127
packages/next-swc/crates/next-core/js/src/entry/app/manifest.ts
Normal file
127
packages/next-swc/crates/next-core/js/src/entry/app/manifest.ts
Normal file
|
@ -0,0 +1,127 @@
|
|||
import type { ClientReferenceManifest } from 'next/dist/build/webpack/plugins/flight-manifest-plugin'
|
||||
|
||||
export function createManifests() {
|
||||
const proxyMethodsForModule = (
|
||||
id: string
|
||||
): ProxyHandler<ClientReferenceManifest['ssrModuleMapping']> => {
|
||||
return {
|
||||
get(_target, prop: string) {
|
||||
return {
|
||||
id,
|
||||
chunks: JSON.parse(id)[1],
|
||||
name: prop,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const availableModules = new Set()
|
||||
const toPath = (chunk: ChunkData) =>
|
||||
typeof chunk === 'string' ? chunk : chunk.path
|
||||
/// determines if a chunk is needed based on the current available modules
|
||||
const filterAvailable = (chunk: ChunkData) => {
|
||||
if (typeof chunk === 'string') {
|
||||
return true
|
||||
} else {
|
||||
let includedList = chunk.included || []
|
||||
if (includedList.length === 0) {
|
||||
return true
|
||||
}
|
||||
let needed = false
|
||||
for (const item of includedList) {
|
||||
if (!availableModules.has(item)) {
|
||||
availableModules.add(item)
|
||||
needed = true
|
||||
}
|
||||
}
|
||||
return needed
|
||||
}
|
||||
}
|
||||
|
||||
const proxyMethodsNested = (
|
||||
type: 'ssrModuleMapping' | 'clientModules' | 'entryCSSFiles'
|
||||
): ProxyHandler<
|
||||
| ClientReferenceManifest['ssrModuleMapping']
|
||||
| ClientReferenceManifest['clientModules']
|
||||
| ClientReferenceManifest['entryCSSFiles']
|
||||
> => {
|
||||
return {
|
||||
get(_target, key: string) {
|
||||
if (type === 'ssrModuleMapping') {
|
||||
return new Proxy({}, proxyMethodsForModule(key as string))
|
||||
}
|
||||
if (type === 'clientModules') {
|
||||
// The key is a `${file}#${name}`, but `file` can contain `#` itself.
|
||||
// There are 2 possibilities:
|
||||
// "file#" => id = "file", name = ""
|
||||
// "file#foo" => id = "file", name = "foo"
|
||||
const pos = key.lastIndexOf('#')
|
||||
let id = key
|
||||
let name = ''
|
||||
if (pos !== -1) {
|
||||
id = key.slice(0, pos)
|
||||
name = key.slice(pos + 1)
|
||||
} else {
|
||||
throw new Error('keys need to be formatted as {file}#{name}')
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
chunks: JSON.parse(id)[1],
|
||||
}
|
||||
}
|
||||
if (type === 'entryCSSFiles') {
|
||||
const cssChunks = JSON.parse(key)
|
||||
// TODO(WEB-856) subscribe to changes
|
||||
return {
|
||||
modules: [],
|
||||
files: cssChunks.filter(filterAvailable).map(toPath),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
const proxyMethods = (): ProxyHandler<ClientReferenceManifest> => {
|
||||
const clientModulesProxy = new Proxy(
|
||||
{},
|
||||
proxyMethodsNested('clientModules')
|
||||
)
|
||||
const ssrModuleMappingProxy = new Proxy(
|
||||
{},
|
||||
proxyMethodsNested('ssrModuleMapping')
|
||||
)
|
||||
const entryCSSFilesProxy = new Proxy(
|
||||
{},
|
||||
proxyMethodsNested('entryCSSFiles')
|
||||
)
|
||||
return {
|
||||
get(_target: any, prop: string) {
|
||||
if (prop === 'ssrModuleMapping') {
|
||||
return ssrModuleMappingProxy
|
||||
}
|
||||
if (prop === 'clientModules') {
|
||||
return clientModulesProxy
|
||||
}
|
||||
if (prop === 'entryCSSFiles') {
|
||||
return entryCSSFilesProxy
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const clientReferenceManifest: ClientReferenceManifest = new Proxy(
|
||||
{} as any,
|
||||
proxyMethods()
|
||||
)
|
||||
|
||||
return { clientReferenceManifest }
|
||||
}
|
||||
|
||||
export function installRequireAndChunkLoad() {
|
||||
globalThis.__next_require__ = (data) => {
|
||||
const [, , ssr_id] = JSON.parse(data)
|
||||
return __turbopack_require__(ssr_id)
|
||||
}
|
||||
globalThis.__next_chunk_load__ = () => Promise.resolve()
|
||||
}
|
|
@ -1,20 +1,6 @@
|
|||
// IPC need to be the first import to allow it to catch errors happening during
|
||||
// the other imports
|
||||
import startHandler from '../../internal/api-server-handler'
|
||||
|
||||
import '../../polyfill/app-polyfills'
|
||||
|
||||
import { parse as parseUrl } from 'node:url'
|
||||
|
||||
import {
|
||||
NodeNextRequest,
|
||||
NodeNextResponse,
|
||||
} from 'next/dist/server/base-http/node'
|
||||
import { sendResponse } from 'next/dist/server/send-response'
|
||||
import { NextRequestAdapter } from 'next/dist/server/web/spec-extension/adapters/next-request'
|
||||
import { RouteHandlerManagerContext } from 'next/dist/server/future/route-handler-managers/route-handler-manager'
|
||||
|
||||
import { attachRequestMeta } from '../../internal/next-request-helpers'
|
||||
import startHandler from '../../internal/nodejs-proxy-handler'
|
||||
|
||||
import RouteModule from 'ROUTE_MODULE'
|
||||
import * as userland from 'ENTRY'
|
||||
|
@ -34,33 +20,4 @@ const routeModule = new RouteModule({
|
|||
nextConfigOutput: undefined,
|
||||
})
|
||||
|
||||
startHandler(async ({ request, response, query, params, path }) => {
|
||||
const req = new NodeNextRequest(request)
|
||||
const res = new NodeNextResponse(response)
|
||||
|
||||
const parsedUrl = parseUrl(req.url!, true)
|
||||
attachRequestMeta(req, parsedUrl, request.headers.host!)
|
||||
|
||||
const context: RouteHandlerManagerContext = {
|
||||
params,
|
||||
prerenderManifest: {
|
||||
version: -1 as any, // letting us know this doesn't conform to spec
|
||||
routes: {},
|
||||
dynamicRoutes: {},
|
||||
notFoundRoutes: [],
|
||||
preview: {
|
||||
previewModeId: 'development-id',
|
||||
} as any,
|
||||
},
|
||||
staticGenerationContext: {
|
||||
supportsDynamicHTML: true,
|
||||
},
|
||||
}
|
||||
|
||||
const routeResponse = await routeModule.handle(
|
||||
NextRequestAdapter.fromNodeNextRequest(req),
|
||||
context
|
||||
)
|
||||
|
||||
await sendResponse(req, res, routeResponse)
|
||||
})
|
||||
startHandler(routeModule)
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from 'next/dist/server/base-http/node'
|
||||
|
||||
import { attachRequestMeta } from '../internal/next-request-helpers'
|
||||
import { runEdgeFunction } from '../internal/edge'
|
||||
import { runEdgeFunction, updateResponse } from '../internal/edge'
|
||||
|
||||
import chunkGroup from 'INNER_EDGE_CHUNK_GROUP'
|
||||
|
||||
|
@ -34,11 +34,10 @@ startHandler(async ({ request, response, query, params, path }) => {
|
|||
assets: [],
|
||||
}
|
||||
|
||||
await runEdgeFunction({
|
||||
const result = await runEdgeFunction({
|
||||
edgeInfo,
|
||||
outputDir: 'pages',
|
||||
req,
|
||||
res,
|
||||
query,
|
||||
params,
|
||||
path,
|
||||
|
@ -46,4 +45,6 @@ startHandler(async ({ request, response, query, params, path }) => {
|
|||
console.warn(warning)
|
||||
},
|
||||
})
|
||||
|
||||
await updateResponse(res, result)
|
||||
})
|
||||
|
|
|
@ -1,48 +1,15 @@
|
|||
// IPC need to be the first import to allow it to catch errors happening during
|
||||
// the other imports
|
||||
import { IPC } from '@vercel/turbopack-node/ipc/index'
|
||||
import startOperationStreamHandler from './operation-stream'
|
||||
|
||||
import type { ClientRequest, IncomingMessage, Server } from 'node:http'
|
||||
import type { ServerResponse } from 'node:http'
|
||||
import { Buffer } from 'node:buffer'
|
||||
|
||||
import type { Ipc } from '@vercel/turbopack-node/ipc/index'
|
||||
|
||||
import type { RenderData } from 'types/turbopack'
|
||||
import { createServer, makeRequest } from '../internal/server'
|
||||
import { toPairs } from '../internal/headers'
|
||||
|
||||
const ipc = IPC as Ipc<IpcIncomingMessage, IpcOutgoingMessage>
|
||||
|
||||
type IpcIncomingMessage =
|
||||
| {
|
||||
type: 'headers'
|
||||
data: RenderData
|
||||
}
|
||||
| {
|
||||
type: 'bodyChunk'
|
||||
data: number[]
|
||||
}
|
||||
| { type: 'bodyEnd' }
|
||||
|
||||
type IpcOutgoingMessage =
|
||||
| {
|
||||
type: 'headers'
|
||||
data: ResponseHeaders
|
||||
}
|
||||
| {
|
||||
type: 'bodyChunk'
|
||||
data: number[]
|
||||
}
|
||||
| {
|
||||
type: 'bodyEnd'
|
||||
}
|
||||
|
||||
type ResponseHeaders = {
|
||||
status: number
|
||||
headers: [string, string][]
|
||||
}
|
||||
|
||||
type Handler = (data: {
|
||||
request: IncomingMessage
|
||||
response: ServerResponse<IncomingMessage>
|
||||
|
@ -58,134 +25,96 @@ type Operation = {
|
|||
server: Server
|
||||
}
|
||||
|
||||
export default function startHandler(handler: Handler): void {
|
||||
;(async () => {
|
||||
while (true) {
|
||||
let operationPromise: Promise<Operation> | null = null
|
||||
|
||||
const msg = await ipc.recv()
|
||||
|
||||
switch (msg.type) {
|
||||
case 'headers': {
|
||||
operationPromise = createOperation(msg.data)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
console.error('unexpected message type', msg.type)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
let body = Buffer.alloc(0)
|
||||
let operation: Operation
|
||||
loop: while (true) {
|
||||
const msg = await ipc.recv()
|
||||
|
||||
switch (msg.type) {
|
||||
case 'bodyChunk': {
|
||||
body = Buffer.concat([body, Buffer.from(msg.data)])
|
||||
break
|
||||
}
|
||||
case 'bodyEnd': {
|
||||
operation = await operationPromise
|
||||
break loop
|
||||
}
|
||||
default: {
|
||||
console.error('unexpected message type', msg.type)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
endOperation(operation, body),
|
||||
operation.clientResponsePromise.then((clientResponse) =>
|
||||
handleClientResponse(operation.server, clientResponse)
|
||||
),
|
||||
])
|
||||
}
|
||||
})().catch((err) => {
|
||||
ipc.sendError(err)
|
||||
})
|
||||
|
||||
async function createOperation(renderData: RenderData): Promise<Operation> {
|
||||
const server = await createServer()
|
||||
|
||||
const {
|
||||
clientRequest,
|
||||
clientResponsePromise,
|
||||
serverRequest,
|
||||
serverResponse,
|
||||
} = await makeRequest(
|
||||
server,
|
||||
renderData.method,
|
||||
renderData.path,
|
||||
renderData.rawQuery,
|
||||
renderData.rawHeaders,
|
||||
renderData.data?.serverInfo
|
||||
)
|
||||
|
||||
return {
|
||||
clientRequest,
|
||||
server,
|
||||
clientResponsePromise,
|
||||
apiOperation: handler({
|
||||
request: serverRequest,
|
||||
response: serverResponse,
|
||||
query: renderData.rawQuery,
|
||||
params: renderData.params,
|
||||
path: renderData.path,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
function handleClientResponse(
|
||||
server: Server,
|
||||
clientResponse: IncomingMessage
|
||||
) {
|
||||
const responseHeaders: ResponseHeaders = {
|
||||
status: clientResponse.statusCode!,
|
||||
headers: toPairs(clientResponse.rawHeaders),
|
||||
}
|
||||
|
||||
ipc.send({
|
||||
type: 'headers',
|
||||
data: responseHeaders,
|
||||
})
|
||||
|
||||
clientResponse.on('data', (chunk) => {
|
||||
ipc.send({
|
||||
type: 'bodyChunk',
|
||||
data: chunk.toJSON().data,
|
||||
})
|
||||
})
|
||||
|
||||
clientResponse.once('end', () => {
|
||||
ipc.send({ type: 'bodyEnd' })
|
||||
server.close()
|
||||
})
|
||||
|
||||
clientResponse.once('error', (err) => {
|
||||
ipc.sendError(err)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends an operation by writing the response body to the client and waiting for the Next.js API resolver to finish.
|
||||
*/
|
||||
async function endOperation(operation: Operation, body: Buffer) {
|
||||
operation.clientRequest.end(body)
|
||||
|
||||
try {
|
||||
await operation.apiOperation
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
await ipc.sendError(error)
|
||||
} else {
|
||||
await ipc.sendError(new Error('an unknown error occurred'))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
type ResponseHeaders = {
|
||||
status: number
|
||||
headers: [string, string][]
|
||||
}
|
||||
|
||||
export default function startHandler(handler: Handler): void {
|
||||
startOperationStreamHandler(
|
||||
async (renderData: RenderData, respond, reportError) => {
|
||||
const operationPromise = (async function createOperation() {
|
||||
const server = await createServer()
|
||||
|
||||
const {
|
||||
clientRequest,
|
||||
clientResponsePromise,
|
||||
serverRequest,
|
||||
serverResponse,
|
||||
} = await makeRequest(
|
||||
server,
|
||||
renderData.method,
|
||||
renderData.path,
|
||||
renderData.rawQuery,
|
||||
renderData.rawHeaders,
|
||||
renderData.data?.serverInfo
|
||||
)
|
||||
|
||||
return {
|
||||
clientRequest,
|
||||
server,
|
||||
clientResponsePromise,
|
||||
apiOperation: handler({
|
||||
request: serverRequest,
|
||||
response: serverResponse,
|
||||
query: renderData.rawQuery,
|
||||
params: renderData.params,
|
||||
path: renderData.path,
|
||||
}),
|
||||
}
|
||||
})()
|
||||
|
||||
function handleClientResponse(
|
||||
server: Server,
|
||||
clientResponse: IncomingMessage
|
||||
) {
|
||||
const responseHeaders: ResponseHeaders = {
|
||||
status: clientResponse.statusCode!,
|
||||
headers: toPairs(clientResponse.rawHeaders),
|
||||
}
|
||||
|
||||
const channel = respond(responseHeaders)
|
||||
|
||||
clientResponse.on('data', (chunk) => {
|
||||
channel.chunk(chunk)
|
||||
})
|
||||
|
||||
clientResponse.once('end', () => {
|
||||
channel.end()
|
||||
server.close()
|
||||
})
|
||||
|
||||
clientResponse.once('error', (err) => {
|
||||
reportError(err)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends an operation by writing the response body to the client and waiting for the Next.js API resolver to finish.
|
||||
*/
|
||||
async function endOperation(operation: Operation, body: Buffer) {
|
||||
operation.clientRequest.end(body)
|
||||
|
||||
try {
|
||||
await operation.apiOperation
|
||||
} catch (error) {
|
||||
await reportError(error)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
streamRequest: false,
|
||||
onBody: async (body) => {
|
||||
const operation = await operationPromise
|
||||
await Promise.all([
|
||||
endOperation(operation, body),
|
||||
operation.clientResponsePromise.then((clientResponse) =>
|
||||
handleClientResponse(operation.server, clientResponse)
|
||||
),
|
||||
])
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import type {
|
|||
} from 'next/dist/server/base-http/node'
|
||||
import { parse, ParsedUrlQuery } from 'querystring'
|
||||
import type { Params } from 'next/dist/shared/lib/router/utils/route-matcher'
|
||||
import { FetchEventResult } from 'next/dist/server/web/types'
|
||||
import type { FetchEventResult } from 'next/dist/server/web/types'
|
||||
import { getCloneableBody } from 'next/dist/server/body-streams'
|
||||
|
||||
// This is an adapted version of a similar function in next-dev-server.
|
||||
|
@ -19,7 +19,6 @@ export async function runEdgeFunction({
|
|||
edgeInfo,
|
||||
outputDir,
|
||||
req,
|
||||
res,
|
||||
query,
|
||||
params,
|
||||
path,
|
||||
|
@ -34,12 +33,11 @@ export async function runEdgeFunction({
|
|||
}
|
||||
outputDir: string
|
||||
req: BaseNextRequest | NodeNextRequest
|
||||
res: BaseNextResponse | NodeNextResponse
|
||||
query: string
|
||||
path: string
|
||||
params: Params | undefined
|
||||
onWarning?: (warning: Error) => void
|
||||
}): Promise<FetchEventResult | null> {
|
||||
}): Promise<FetchEventResult> {
|
||||
// For edge to "fetch" we must always provide an absolute URL
|
||||
const initialUrl = new URL(path, 'http://n')
|
||||
const parsedQuery = parse(query)
|
||||
|
@ -83,21 +81,28 @@ export async function runEdgeFunction({
|
|||
onWarning,
|
||||
})) as FetchEventResult
|
||||
|
||||
res.statusCode = result.response.status
|
||||
res.statusMessage = result.response.statusText
|
||||
return result
|
||||
}
|
||||
|
||||
export async function updateResponse(
|
||||
response: BaseNextResponse<any> | NodeNextResponse,
|
||||
result: FetchEventResult
|
||||
) {
|
||||
response.statusCode = result.response.status
|
||||
response.statusMessage = result.response.statusText
|
||||
|
||||
result.response.headers.forEach((value: string, key) => {
|
||||
// the append handling is special cased for `set-cookie`
|
||||
if (key.toLowerCase() === 'set-cookie') {
|
||||
res.setHeader(key, value)
|
||||
response.setHeader(key, value)
|
||||
} else {
|
||||
res.appendHeader(key, value)
|
||||
response.appendHeader(key, value)
|
||||
}
|
||||
})
|
||||
|
||||
if (result.response.body) {
|
||||
// TODO(gal): not sure that we always need to stream
|
||||
const nodeResStream = (res as NodeNextResponse).originalResponse
|
||||
const nodeResStream = (response as NodeNextResponse).originalResponse
|
||||
const {
|
||||
consumeUint8ArrayReadableStream,
|
||||
} = require('next/dist/compiled/edge-runtime')
|
||||
|
@ -111,10 +116,8 @@ export async function runEdgeFunction({
|
|||
nodeResStream.end()
|
||||
}
|
||||
} else {
|
||||
;(res as NodeNextResponse).originalResponse.end()
|
||||
;(response as NodeNextResponse).originalResponse.end()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function stringifyUrlQueryParam(param: unknown): string {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// IPC need to be the first import to allow it to catch errors happening during
|
||||
// the other imports
|
||||
import startHandler from './api-server-handler'
|
||||
|
||||
import '../polyfill/app-polyfills'
|
||||
|
||||
import { parse as parseUrl } from 'node:url'
|
||||
|
||||
import {
|
||||
NodeNextRequest,
|
||||
NodeNextResponse,
|
||||
} from 'next/dist/server/base-http/node'
|
||||
import { sendResponse } from 'next/dist/server/send-response'
|
||||
import { NextRequestAdapter } from 'next/dist/server/web/spec-extension/adapters/next-request'
|
||||
import { RouteHandlerManagerContext } from 'next/dist/server/future/route-handler-managers/route-handler-manager'
|
||||
|
||||
import { attachRequestMeta } from './next-request-helpers'
|
||||
|
||||
import type { RouteModule } from 'next/dist/server/future/route-modules/route-module'
|
||||
|
||||
export default (routeModule: RouteModule) => {
|
||||
startHandler(async ({ request, response, params }) => {
|
||||
const req = new NodeNextRequest(request)
|
||||
const res = new NodeNextResponse(response)
|
||||
|
||||
const parsedUrl = parseUrl(req.url!, true)
|
||||
attachRequestMeta(req, parsedUrl, request.headers.host!)
|
||||
|
||||
const context: RouteHandlerManagerContext = {
|
||||
params,
|
||||
prerenderManifest: {
|
||||
version: -1 as any, // letting us know this doesn't conform to spec
|
||||
routes: {},
|
||||
dynamicRoutes: {},
|
||||
notFoundRoutes: [],
|
||||
preview: {
|
||||
previewModeId: 'development-id',
|
||||
} as any,
|
||||
},
|
||||
staticGenerationContext: {
|
||||
supportsDynamicHTML: true,
|
||||
},
|
||||
}
|
||||
|
||||
const routeResponse = await routeModule.handle(
|
||||
NextRequestAdapter.fromNodeNextRequest(req),
|
||||
context
|
||||
)
|
||||
|
||||
await sendResponse(req, res, routeResponse)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
// IPC need to be the first import to allow it to catch errors happening during
|
||||
// the other imports
|
||||
import { IPC } from '@vercel/turbopack-node/ipc/index'
|
||||
|
||||
import { Buffer } from 'node:buffer'
|
||||
|
||||
import type { Ipc } from '@vercel/turbopack-node/ipc/index'
|
||||
|
||||
type Operation =
|
||||
| {
|
||||
streamRequest: true
|
||||
onChunk?: (chunk: Buffer) => Promise<void>
|
||||
onEnd?: () => Promise<void>
|
||||
}
|
||||
| {
|
||||
streamRequest?: false
|
||||
onBody?: (body: Buffer) => Promise<void>
|
||||
}
|
||||
|
||||
type IpcIncomingMessage<T> =
|
||||
| {
|
||||
type: 'headers'
|
||||
data: T
|
||||
}
|
||||
| {
|
||||
type: 'bodyChunk'
|
||||
data: number[]
|
||||
}
|
||||
| {
|
||||
type: 'bodyText'
|
||||
data: number[]
|
||||
}
|
||||
| { type: 'bodyEnd' }
|
||||
|
||||
type IpcOutgoingMessage<R> =
|
||||
| {
|
||||
type: 'headers'
|
||||
data: R
|
||||
}
|
||||
| {
|
||||
type: 'bodyChunk'
|
||||
data: number[]
|
||||
}
|
||||
| {
|
||||
type: 'bodyEnd'
|
||||
}
|
||||
|
||||
interface Response {
|
||||
chunk: (data: Buffer) => Promise<void>
|
||||
end: () => Promise<void>
|
||||
}
|
||||
|
||||
export default function startHandler<T, R>(
|
||||
createOperation: (
|
||||
data: T,
|
||||
respond: (data: R) => Response,
|
||||
reportError: (error: unknown) => Promise<never>
|
||||
) => Promise<Operation | void>
|
||||
) {
|
||||
const ipc = IPC as Ipc<IpcIncomingMessage<T>, IpcOutgoingMessage<R>>
|
||||
|
||||
;(async () => {
|
||||
while (true) {
|
||||
let operation: Operation | void
|
||||
|
||||
{
|
||||
const msg = await ipc.recv()
|
||||
|
||||
switch (msg.type) {
|
||||
case 'headers': {
|
||||
operation = await createOperation(
|
||||
msg.data,
|
||||
(data) => {
|
||||
ipc.send({
|
||||
type: 'headers',
|
||||
data,
|
||||
})
|
||||
return {
|
||||
chunk: (buf: Buffer) => {
|
||||
return ipc.send({
|
||||
type: 'bodyChunk',
|
||||
data: buf.toJSON().data,
|
||||
})
|
||||
},
|
||||
end: () => {
|
||||
return ipc.send({ type: 'bodyEnd' })
|
||||
},
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
return ipc.sendError(
|
||||
error instanceof Error
|
||||
? error
|
||||
: new Error(`an unknown error occurred: ${error}`)
|
||||
)
|
||||
}
|
||||
)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
console.error('unexpected message type', msg.type)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (operation) {
|
||||
if (operation.streamRequest) {
|
||||
loop: while (true) {
|
||||
const msg = await ipc.recv()
|
||||
|
||||
switch (msg.type) {
|
||||
case 'bodyChunk':
|
||||
case 'bodyText': {
|
||||
await operation.onChunk?.(Buffer.from(msg.data))
|
||||
break
|
||||
}
|
||||
case 'bodyEnd': {
|
||||
await operation.onEnd?.()
|
||||
break loop
|
||||
}
|
||||
default: {
|
||||
console.error('unexpected message type', msg.type)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let body = Buffer.alloc(0)
|
||||
loop: while (true) {
|
||||
const msg = await ipc.recv()
|
||||
|
||||
switch (msg.type) {
|
||||
case 'bodyChunk':
|
||||
case 'bodyText': {
|
||||
body = Buffer.concat([body, Buffer.from(msg.data)])
|
||||
break
|
||||
}
|
||||
case 'bodyEnd': {
|
||||
await operation.onBody?.(body)
|
||||
break loop
|
||||
}
|
||||
default: {
|
||||
console.error('unexpected message type', msg.type)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})().catch((err) => {
|
||||
ipc.sendError(err)
|
||||
})
|
||||
}
|
|
@ -27,8 +27,7 @@
|
|||
"@vercel/turbopack-next/pages/_app": ["node_modules/next/app"],
|
||||
"@vercel/turbopack-next/pages/_document": ["node_modules/next/document"],
|
||||
"@vercel/turbopack-next/pages/_error": ["node_modules/next/error"],
|
||||
"@vercel/turbopack-next/internal/_error": ["node_modules/next/error"],
|
||||
"next/*": ["node_modules/next/*"]
|
||||
"@vercel/turbopack-next/internal/_error": ["node_modules/next/error"]
|
||||
},
|
||||
"resolveJsonModule": true,
|
||||
"types": ["react/next"],
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
declare module 'next/dist/compiled/react-server-dom-webpack/client'
|
||||
declare module 'next/dist/client/app-call-server'
|
||||
|
|
|
@ -80,6 +80,17 @@ declare module 'BOOTSTRAP_CONFIG' {
|
|||
export const KIND: RouteKind
|
||||
}
|
||||
|
||||
declare module 'APP_BOOTSTRAP' {
|
||||
const chunks: Array<string>
|
||||
export default chunks
|
||||
}
|
||||
|
||||
declare module 'APP_ENTRY' {
|
||||
export const tree: any
|
||||
export const pathname: string
|
||||
// and more...
|
||||
}
|
||||
|
||||
declare module 'CLIENT_MODULE' {
|
||||
export const __turbopack_module_id__: string
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||
|
||||
use turbopack_binding::turbo::tasks_fs::FileSystemPathVc;
|
||||
|
||||
pub mod next_layout_entry_transition;
|
||||
pub mod next_server_component_transition;
|
||||
|
||||
#[turbo_tasks::value(shared)]
|
||||
pub struct LayoutSegment {
|
||||
|
|
|
@ -13,7 +13,10 @@ use turbopack_binding::{
|
|||
},
|
||||
};
|
||||
|
||||
use crate::next_client_component::with_client_chunks::WithClientChunksAsset;
|
||||
use crate::next_client_component::{
|
||||
with_chunking_context_scope_asset::WithChunkingContextScopeAsset,
|
||||
with_client_chunks::WithClientChunksAsset,
|
||||
};
|
||||
|
||||
#[turbo_tasks::value(shared)]
|
||||
pub struct NextServerComponentTransition {
|
||||
|
@ -59,10 +62,15 @@ impl Transition for NextServerComponentTransition {
|
|||
bail!("Not an ecmascript module");
|
||||
};
|
||||
|
||||
Ok(WithClientChunksAsset {
|
||||
asset,
|
||||
// next.js code already adds _next prefix
|
||||
server_root: self.server_root.join("_next"),
|
||||
Ok(WithChunkingContextScopeAsset {
|
||||
asset: WithClientChunksAsset {
|
||||
asset,
|
||||
// next.js code already adds _next prefix
|
||||
server_root: self.server_root.join("_next"),
|
||||
}
|
||||
.cell()
|
||||
.into(),
|
||||
layer: "rsc".to_string(),
|
||||
}
|
||||
.cell()
|
||||
.into())
|
|
@ -1,21 +1,24 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{bail, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use swc_core::{
|
||||
common::{source_map::Pos, Span, Spanned},
|
||||
ecma::ast::{Expr, Ident, Program},
|
||||
};
|
||||
use turbo_tasks::{primitives::StringVc, trace::TraceRawVcs};
|
||||
use turbo_tasks::{primitives::StringVc, trace::TraceRawVcs, TryJoinIterExt};
|
||||
use turbo_tasks_fs::FileSystemPathVc;
|
||||
use turbopack_binding::turbopack::{
|
||||
core::{
|
||||
asset::{Asset, AssetVc},
|
||||
context::{AssetContext, AssetContextVc},
|
||||
ident::AssetIdentVc,
|
||||
issue::{
|
||||
Issue, IssueSeverity, IssueSeverityVc, IssueSourceVc, IssueVc, OptionIssueSourceVc,
|
||||
},
|
||||
reference_type::{EcmaScriptModulesReferenceSubType, ReferenceType},
|
||||
source_asset::SourceAssetVc,
|
||||
},
|
||||
ecmascript::{
|
||||
analyzer::{graph::EvalContext, JsValue},
|
||||
|
@ -24,7 +27,7 @@ use turbopack_binding::turbopack::{
|
|||
},
|
||||
};
|
||||
|
||||
use crate::util::NextRuntime;
|
||||
use crate::{app_structure::LoaderTreeVc, util::NextRuntime};
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, TraceRawVcs, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
|
@ -50,15 +53,14 @@ pub enum NextSegmentFetchCache {
|
|||
}
|
||||
|
||||
#[turbo_tasks::value]
|
||||
#[derive(Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct NextSegmentConfig {
|
||||
pub dynamic: NextSegmentDynamic,
|
||||
pub dynamic_params: bool,
|
||||
pub revalidate: bool,
|
||||
pub fetch_cache: NextSegmentFetchCache,
|
||||
pub runtime: NextRuntime,
|
||||
pub referred_region: String,
|
||||
pub dynamic: Option<NextSegmentDynamic>,
|
||||
pub dynamic_params: Option<bool>,
|
||||
pub revalidate: Option<bool>,
|
||||
pub fetch_cache: Option<NextSegmentFetchCache>,
|
||||
pub runtime: Option<NextRuntime>,
|
||||
pub preferred_region: Option<String>,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
|
@ -69,16 +71,73 @@ impl NextSegmentConfigVc {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for NextSegmentConfig {
|
||||
fn default() -> Self {
|
||||
NextSegmentConfig {
|
||||
dynamic: Default::default(),
|
||||
dynamic_params: true,
|
||||
revalidate: false,
|
||||
fetch_cache: Default::default(),
|
||||
runtime: Default::default(),
|
||||
referred_region: "auto".to_string(),
|
||||
impl NextSegmentConfig {
|
||||
/// Applies the parent config to this config, setting any unset values to
|
||||
/// the parent's values.
|
||||
pub fn apply_parent_config(&mut self, parent: &Self) {
|
||||
let NextSegmentConfig {
|
||||
dynamic,
|
||||
dynamic_params,
|
||||
revalidate,
|
||||
fetch_cache,
|
||||
runtime,
|
||||
preferred_region,
|
||||
} = self;
|
||||
*dynamic = dynamic.or(parent.dynamic);
|
||||
*dynamic_params = dynamic_params.or(parent.dynamic_params);
|
||||
*revalidate = revalidate.or(parent.revalidate);
|
||||
*fetch_cache = fetch_cache.or(parent.fetch_cache);
|
||||
*runtime = runtime.or(parent.runtime);
|
||||
*preferred_region = preferred_region.take().or(parent.preferred_region.clone());
|
||||
}
|
||||
|
||||
/// Applies a config from a paralllel route to this config, returning an
|
||||
/// error if there are conflicting values.
|
||||
pub fn apply_parallel_config(&mut self, parallel_config: &Self) -> Result<()> {
|
||||
fn merge_parallel<T: PartialEq + Clone>(
|
||||
a: &mut Option<T>,
|
||||
b: &Option<T>,
|
||||
name: &str,
|
||||
) -> Result<()> {
|
||||
match (a.as_ref(), b) {
|
||||
(Some(a), Some(b)) => {
|
||||
if *a != *b {
|
||||
bail!(
|
||||
"Sibling segment configs have conflicting values for {}",
|
||||
name
|
||||
)
|
||||
}
|
||||
}
|
||||
(None, Some(b)) => {
|
||||
*a = Some(b.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
let Self {
|
||||
dynamic,
|
||||
dynamic_params,
|
||||
revalidate,
|
||||
fetch_cache,
|
||||
runtime,
|
||||
preferred_region,
|
||||
} = self;
|
||||
merge_parallel(dynamic, ¶llel_config.dynamic, "dynamic")?;
|
||||
merge_parallel(
|
||||
dynamic_params,
|
||||
¶llel_config.dynamic_params,
|
||||
"dynamicParams",
|
||||
)?;
|
||||
merge_parallel(revalidate, ¶llel_config.revalidate, "revalidate")?;
|
||||
merge_parallel(fetch_cache, ¶llel_config.fetch_cache, "fetchCache")?;
|
||||
merge_parallel(runtime, ¶llel_config.runtime, "runtime")?;
|
||||
merge_parallel(
|
||||
preferred_region,
|
||||
¶llel_config.preferred_region,
|
||||
"referredRegion",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,7 +275,7 @@ fn parse_config_value(
|
|||
};
|
||||
|
||||
config.dynamic = match serde_json::from_value(Value::String(val.to_string())) {
|
||||
Ok(dynamic) => dynamic,
|
||||
Ok(dynamic) => Some(dynamic),
|
||||
Err(err) => {
|
||||
return invalid_config(
|
||||
&format!("`dynamic` has an invalid value: {}", err),
|
||||
|
@ -231,7 +290,7 @@ fn parse_config_value(
|
|||
return invalid_config("`dynamicParams` needs to be a static boolean", &value);
|
||||
};
|
||||
|
||||
config.dynamic_params = val;
|
||||
config.dynamic_params = Some(val);
|
||||
}
|
||||
"revalidate" => {
|
||||
let value = eval_context.eval(init);
|
||||
|
@ -239,7 +298,7 @@ fn parse_config_value(
|
|||
return invalid_config("`revalidate` needs to be a static boolean", &value);
|
||||
};
|
||||
|
||||
config.revalidate = val;
|
||||
config.revalidate = Some(val);
|
||||
}
|
||||
"fetchCache" => {
|
||||
let value = eval_context.eval(init);
|
||||
|
@ -248,7 +307,7 @@ fn parse_config_value(
|
|||
};
|
||||
|
||||
config.fetch_cache = match serde_json::from_value(Value::String(val.to_string())) {
|
||||
Ok(fetch_cache) => fetch_cache,
|
||||
Ok(fetch_cache) => Some(fetch_cache),
|
||||
Err(err) => {
|
||||
return invalid_config(
|
||||
&format!("`fetchCache` has an invalid value: {}", err),
|
||||
|
@ -264,7 +323,7 @@ fn parse_config_value(
|
|||
};
|
||||
|
||||
config.runtime = match serde_json::from_value(Value::String(val.to_string())) {
|
||||
Ok(runtime) => runtime,
|
||||
Ok(runtime) => Some(runtime),
|
||||
Err(err) => {
|
||||
return invalid_config(
|
||||
&format!("`runtime` has an invalid value: {}", err),
|
||||
|
@ -279,8 +338,43 @@ fn parse_config_value(
|
|||
return invalid_config("`preferredRegion` needs to be a static string", &value);
|
||||
};
|
||||
|
||||
config.referred_region = val.to_string();
|
||||
config.preferred_region = Some(val.to_string());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub async fn parse_segment_config_from_loader_tree(
|
||||
loader_tree: LoaderTreeVc,
|
||||
context: AssetContextVc,
|
||||
) -> Result<NextSegmentConfigVc> {
|
||||
let loader_tree = loader_tree.await?;
|
||||
let components = loader_tree.components.await?;
|
||||
let mut config = NextSegmentConfig::default();
|
||||
let parallel_configs = loader_tree
|
||||
.parallel_routes
|
||||
.values()
|
||||
.copied()
|
||||
.map(|tree| parse_segment_config_from_loader_tree(tree, context))
|
||||
.try_join()
|
||||
.await?;
|
||||
for tree in parallel_configs {
|
||||
config.apply_parallel_config(&tree)?;
|
||||
}
|
||||
for component in [components.page, components.default, components.layout]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
config.apply_parent_config(
|
||||
&*parse_segment_config_from_source(context.process(
|
||||
SourceAssetVc::new(component).into(),
|
||||
turbo_tasks::Value::new(ReferenceType::EcmaScriptModules(
|
||||
EcmaScriptModulesReferenceSubType::Undefined,
|
||||
)),
|
||||
))
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
Ok(config.cell())
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
use std::{collections::HashMap, io::Write, iter::once};
|
||||
use std::{collections::HashMap, io::Write as _, iter::once};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use async_recursion::async_recursion;
|
||||
use indexmap::{indexmap, IndexMap};
|
||||
use indoc::indoc;
|
||||
use turbo_tasks::{primitives::JsonValueVc, TryJoinIterExt, ValueToString};
|
||||
use turbopack_binding::{
|
||||
turbo::{
|
||||
tasks::{primitives::StringVc, Value},
|
||||
tasks_env::{CustomProcessEnvVc, EnvMapVc, ProcessEnvVc},
|
||||
tasks_fs::{rope::RopeBuilder, File, FileContent, FileSystemPathVc},
|
||||
tasks_fs::{rope::RopeBuilder, File, FileSystemPathVc},
|
||||
},
|
||||
turbopack::{
|
||||
core::{
|
||||
|
@ -59,14 +60,14 @@ use turbopack_binding::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
app_render::next_layout_entry_transition::NextServerComponentTransition,
|
||||
app_segment_config::parse_segment_config_from_source,
|
||||
app_render::next_server_component_transition::NextServerComponentTransition,
|
||||
app_segment_config::{parse_segment_config_from_loader_tree, parse_segment_config_from_source},
|
||||
app_structure::{
|
||||
get_entrypoints, get_global_metadata, Components, Entrypoint, GlobalMetadataVc, LoaderTree,
|
||||
LoaderTreeVc, Metadata, MetadataItem, MetadataWithAltItem, OptionAppDirVc,
|
||||
},
|
||||
bootstrap::{route_bootstrap, BootstrapConfigVc},
|
||||
embed_js::{next_asset, next_js_file, next_js_file_path},
|
||||
embed_js::{next_asset, next_js_file_path},
|
||||
env::env_for_js,
|
||||
fallback::get_fallback_page,
|
||||
mode::NextMode,
|
||||
|
@ -86,7 +87,8 @@ use crate::{
|
|||
next_config::NextConfigVc,
|
||||
next_edge::{
|
||||
context::{get_edge_compile_time_info, get_edge_resolve_options_context},
|
||||
transition::NextEdgeTransition,
|
||||
page_transition::NextEdgePageTransition,
|
||||
route_transition::NextEdgeRouteTransition,
|
||||
},
|
||||
next_image::module::{BlurPlaceholderMode, StructuredImageModuleType},
|
||||
next_route_matcher::{NextFallbackMatcherVc, NextParamsMatcherVc},
|
||||
|
@ -200,7 +202,7 @@ fn next_ssr_client_module_transition(
|
|||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn next_layout_entry_transition(
|
||||
fn next_server_component_transition(
|
||||
project_path: FileSystemPathVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
app_dir: FileSystemPathVc,
|
||||
|
@ -227,6 +229,37 @@ fn next_layout_entry_transition(
|
|||
.into()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn next_edge_server_component_transition(
|
||||
project_path: FileSystemPathVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
app_dir: FileSystemPathVc,
|
||||
server_root: FileSystemPathVc,
|
||||
next_config: NextConfigVc,
|
||||
server_addr: ServerAddrVc,
|
||||
) -> TransitionVc {
|
||||
let ty = Value::new(ServerContextType::AppRSC { app_dir });
|
||||
let mode = NextMode::Development;
|
||||
let rsc_compile_time_info = get_edge_compile_time_info(
|
||||
project_path,
|
||||
server_addr,
|
||||
Value::new(EnvironmentIntention::ServerRendering),
|
||||
);
|
||||
let rsc_resolve_options_context =
|
||||
get_edge_resolve_options_context(project_path, ty, next_config, execution_context);
|
||||
let rsc_module_options_context =
|
||||
get_server_module_options_context(project_path, execution_context, ty, mode, next_config);
|
||||
|
||||
NextServerComponentTransition {
|
||||
rsc_compile_time_info,
|
||||
rsc_module_options_context,
|
||||
rsc_resolve_options_context,
|
||||
server_root,
|
||||
}
|
||||
.cell()
|
||||
.into()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn next_edge_route_transition(
|
||||
project_path: FileSystemPathVc,
|
||||
|
@ -252,12 +285,12 @@ fn next_edge_route_transition(
|
|||
get_client_assets_path(server_root, Value::new(ClientContextType::App { app_dir })),
|
||||
edge_compile_time_info.environment(),
|
||||
)
|
||||
.reference_chunk_source_maps(false)
|
||||
.reference_chunk_source_maps(should_debug("app_source"))
|
||||
.build();
|
||||
let edge_resolve_options_context =
|
||||
get_edge_resolve_options_context(project_path, server_ty, next_config, execution_context);
|
||||
|
||||
NextEdgeTransition {
|
||||
NextEdgeRouteTransition {
|
||||
edge_compile_time_info,
|
||||
edge_chunking_context,
|
||||
edge_module_options_context: None,
|
||||
|
@ -271,6 +304,49 @@ fn next_edge_route_transition(
|
|||
.into()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn next_edge_page_transition(
|
||||
project_path: FileSystemPathVc,
|
||||
app_dir: FileSystemPathVc,
|
||||
server_root: FileSystemPathVc,
|
||||
next_config: NextConfigVc,
|
||||
server_addr: ServerAddrVc,
|
||||
output_path: FileSystemPathVc,
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> TransitionVc {
|
||||
let server_ty = Value::new(ServerContextType::AppRoute { app_dir });
|
||||
|
||||
let edge_compile_time_info = get_edge_compile_time_info(
|
||||
project_path,
|
||||
server_addr,
|
||||
Value::new(EnvironmentIntention::ServerRendering),
|
||||
);
|
||||
|
||||
let edge_chunking_context = DevChunkingContextVc::builder(
|
||||
project_path,
|
||||
output_path.join("edge-pages"),
|
||||
output_path.join("edge-pages/chunks"),
|
||||
get_client_assets_path(server_root, Value::new(ClientContextType::App { app_dir })),
|
||||
edge_compile_time_info.environment(),
|
||||
)
|
||||
.layer("ssr")
|
||||
.reference_chunk_source_maps(should_debug("app_source"))
|
||||
.build();
|
||||
let edge_resolve_options_context =
|
||||
get_edge_resolve_options_context(project_path, server_ty, next_config, execution_context);
|
||||
|
||||
NextEdgePageTransition {
|
||||
edge_compile_time_info,
|
||||
edge_chunking_context,
|
||||
edge_module_options_context: None,
|
||||
edge_resolve_options_context,
|
||||
output_path,
|
||||
bootstrap_asset: next_asset("entry/app/edge-page-bootstrap.ts"),
|
||||
}
|
||||
.cell()
|
||||
.into()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[turbo_tasks::function]
|
||||
fn app_context(
|
||||
|
@ -302,8 +378,20 @@ fn app_context(
|
|||
),
|
||||
);
|
||||
transitions.insert(
|
||||
"next-layout-entry".to_string(),
|
||||
next_layout_entry_transition(
|
||||
"next-edge-page".to_string(),
|
||||
next_edge_page_transition(
|
||||
project_path,
|
||||
app_dir,
|
||||
server_root,
|
||||
next_config,
|
||||
server_addr,
|
||||
output_path,
|
||||
execution_context,
|
||||
),
|
||||
);
|
||||
transitions.insert(
|
||||
"next-server-component".to_string(),
|
||||
next_server_component_transition(
|
||||
project_path,
|
||||
execution_context,
|
||||
app_dir,
|
||||
|
@ -313,6 +401,17 @@ fn app_context(
|
|||
server_addr,
|
||||
),
|
||||
);
|
||||
transitions.insert(
|
||||
"next-edge-server-component".to_string(),
|
||||
next_edge_server_component_transition(
|
||||
project_path,
|
||||
execution_context,
|
||||
app_dir,
|
||||
server_root,
|
||||
next_config,
|
||||
server_addr,
|
||||
),
|
||||
);
|
||||
transitions.insert(
|
||||
"server-to-client".to_string(),
|
||||
next_server_to_client_transition,
|
||||
|
@ -716,6 +815,14 @@ impl AppRendererVc {
|
|||
(context_ssr, intermediate_output_path)
|
||||
};
|
||||
|
||||
let config = parse_segment_config_from_loader_tree(loader_tree, context);
|
||||
|
||||
let runtime = config.await?.runtime;
|
||||
let rsc_transition = match runtime {
|
||||
Some(NextRuntime::NodeJs) | None => "next-server-component",
|
||||
Some(NextRuntime::Edge) => "next-edge-server-component",
|
||||
};
|
||||
|
||||
struct State {
|
||||
inner_assets: IndexMap<String, AssetVc>,
|
||||
counter: usize,
|
||||
|
@ -723,6 +830,7 @@ impl AppRendererVc {
|
|||
loader_tree_code: String,
|
||||
context: AssetContextVc,
|
||||
unsupported_metadata: Vec<FileSystemPathVc>,
|
||||
rsc_transition: &'static str,
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
@ -740,6 +848,7 @@ impl AppRendererVc {
|
|||
loader_tree_code: String::new(),
|
||||
context,
|
||||
unsupported_metadata: Vec::new(),
|
||||
rsc_transition,
|
||||
};
|
||||
|
||||
fn write_component(
|
||||
|
@ -767,7 +876,7 @@ import {}, {{ chunks as {} }} from "COMPONENT_{}";
|
|||
|
||||
state.inner_assets.insert(
|
||||
format!("COMPONENT_{i}"),
|
||||
state.context.with_transition("next-layout-entry").process(
|
||||
state.context.with_transition(state.rsc_transition).process(
|
||||
SourceAssetVc::new(component).into(),
|
||||
Value::new(ReferenceType::EcmaScriptModules(
|
||||
EcmaScriptModulesReferenceSubType::Undefined,
|
||||
|
@ -958,7 +1067,7 @@ import {}, {{ chunks as {} }} from "COMPONENT_{}";
|
|||
walk_tree(&mut state, loader_tree).await?;
|
||||
|
||||
let State {
|
||||
mut inner_assets,
|
||||
inner_assets,
|
||||
imports,
|
||||
loader_tree_code,
|
||||
unsupported_metadata,
|
||||
|
@ -975,41 +1084,29 @@ import {}, {{ chunks as {} }} from "COMPONENT_{}";
|
|||
.emit();
|
||||
}
|
||||
|
||||
// IPC need to be the first import to allow it to catch errors happening during
|
||||
// the other imports
|
||||
let mut result =
|
||||
RopeBuilder::from("import { IPC } from \"@vercel/turbopack-node/ipc/index\";\n");
|
||||
|
||||
let import_path = next_js_file_path("entry")
|
||||
.await?
|
||||
.get_relative_path_to(&*next_js_file_path("polyfill/app-polyfills.ts").await?)
|
||||
.unwrap();
|
||||
writeln!(result, "import \"{import_path}\";\n",)?;
|
||||
let mut result = RopeBuilder::from(indoc! {"
|
||||
\"TURBOPACK { chunking-type: isolatedParallel; transition: next-edge-server-component }\";
|
||||
import GlobalErrorMod from \"next/dist/client/components/error-boundary\"
|
||||
const { GlobalError } = GlobalErrorMod;
|
||||
\"TURBOPACK { chunking-type: isolatedParallel; transition: next-edge-server-component }\";
|
||||
import base from \"next/dist/server/app-render/entry-base\"\n
|
||||
"});
|
||||
|
||||
for import in imports {
|
||||
writeln!(result, "{import}")?;
|
||||
}
|
||||
|
||||
writeln!(result, "const LOADER_TREE = {loader_tree_code};\n")?;
|
||||
writeln!(result, "import BOOTSTRAP from \"BOOTSTRAP\";\n")?;
|
||||
|
||||
inner_assets.insert(
|
||||
"BOOTSTRAP".to_string(),
|
||||
context.with_transition("next-client").process(
|
||||
SourceAssetVc::new(next_js_file_path("entry/app/hydrate.tsx")).into(),
|
||||
Value::new(ReferenceType::EcmaScriptModules(
|
||||
EcmaScriptModulesReferenceSubType::Undefined,
|
||||
)),
|
||||
),
|
||||
);
|
||||
|
||||
let base_code = next_js_file("entry/app-renderer.tsx");
|
||||
if let FileContent::Content(base_file) = &*base_code.await? {
|
||||
result += base_file.content()
|
||||
}
|
||||
writeln!(result, "const tree = {loader_tree_code};\n")?;
|
||||
writeln!(result, "const pathname = '';\n")?;
|
||||
writeln!(
|
||||
result,
|
||||
// Need this hack because "export *" from CommonJS will trigger a warning
|
||||
// otherwise
|
||||
"__turbopack_export_value__({{ tree, GlobalError, pathname, ...base }});\n"
|
||||
)?;
|
||||
|
||||
let file = File::from(result.build());
|
||||
let asset = VirtualAssetVc::new(next_js_file_path("entry/app-renderer.tsx"), file.into());
|
||||
let asset = VirtualAssetVc::new(next_js_file_path("entry/app-entry.tsx"), file.into());
|
||||
|
||||
let chunking_context = DevChunkingContextVc::builder(
|
||||
project_path,
|
||||
|
@ -1019,15 +1116,38 @@ import {}, {{ chunks as {} }} from "COMPONENT_{}";
|
|||
context.compile_time_info().environment(),
|
||||
)
|
||||
.layer("ssr")
|
||||
.reference_chunk_source_maps(false)
|
||||
.reference_chunk_source_maps(should_debug("app_source"))
|
||||
.build();
|
||||
|
||||
let module = context.process(
|
||||
asset.into(),
|
||||
Value::new(ReferenceType::Internal(InnerAssetsVc::cell(inner_assets))),
|
||||
);
|
||||
let renderer_module = match runtime {
|
||||
Some(NextRuntime::NodeJs) | None => context.process(
|
||||
SourceAssetVc::new(next_js_file_path("entry/app-renderer.tsx")).into(),
|
||||
Value::new(ReferenceType::Internal(InnerAssetsVc::cell(indexmap! {
|
||||
"APP_ENTRY".to_string() => context.with_transition(rsc_transition).process(
|
||||
asset.into(),
|
||||
Value::new(ReferenceType::Internal(InnerAssetsVc::cell(inner_assets))),
|
||||
),
|
||||
"APP_BOOTSTRAP".to_string() => context.with_transition("next-client").process(
|
||||
SourceAssetVc::new(next_js_file_path("entry/app/hydrate.tsx")).into(),
|
||||
Value::new(ReferenceType::EcmaScriptModules(
|
||||
EcmaScriptModulesReferenceSubType::Undefined,
|
||||
)),
|
||||
),
|
||||
}))),
|
||||
),
|
||||
Some(NextRuntime::Edge) =>
|
||||
context.process(
|
||||
SourceAssetVc::new(next_js_file_path("entry/app-edge-renderer.tsx")).into(),
|
||||
Value::new(ReferenceType::Internal(InnerAssetsVc::cell(indexmap! {
|
||||
"INNER_EDGE_CHUNK_GROUP".to_string() => context.with_transition("next-edge-page").process(
|
||||
asset.into(),
|
||||
Value::new(ReferenceType::Internal(InnerAssetsVc::cell(inner_assets))),
|
||||
),
|
||||
}))),
|
||||
)
|
||||
};
|
||||
|
||||
let Some(module) = EvaluatableAssetVc::resolve_from(module).await? else {
|
||||
let Some(module) = EvaluatableAssetVc::resolve_from(renderer_module).await? else {
|
||||
bail!("internal module must be evaluatable");
|
||||
};
|
||||
|
||||
|
@ -1096,7 +1216,7 @@ impl AppRouteVc {
|
|||
this.context.compile_time_info().environment(),
|
||||
)
|
||||
.layer("ssr")
|
||||
.reference_chunk_source_maps(false)
|
||||
.reference_chunk_source_maps(should_debug("app_source"))
|
||||
.build();
|
||||
|
||||
let entry_source_asset = SourceAssetVc::new(this.entry_path);
|
||||
|
@ -1107,7 +1227,7 @@ impl AppRouteVc {
|
|||
|
||||
let config = parse_segment_config_from_source(entry_asset);
|
||||
let module = match config.await?.runtime {
|
||||
NextRuntime::NodeJs => {
|
||||
Some(NextRuntime::NodeJs) | None => {
|
||||
let bootstrap_asset = next_asset("entry/app/route.ts");
|
||||
|
||||
route_bootstrap(
|
||||
|
@ -1118,7 +1238,7 @@ impl AppRouteVc {
|
|||
BootstrapConfigVc::empty(),
|
||||
)
|
||||
}
|
||||
NextRuntime::Edge => {
|
||||
Some(NextRuntime::Edge) => {
|
||||
let internal_asset = next_asset("entry/app/edge-route.ts");
|
||||
|
||||
let entry = this.context.with_transition("next-edge-route").process(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use anyhow::{bail, Result};
|
||||
use indexmap::{indexmap, IndexMap};
|
||||
use turbo_tasks::Value;
|
||||
use turbo_tasks::{Value, ValueToString};
|
||||
use turbo_tasks_fs::{File, FileSystemPathVc};
|
||||
use turbopack_binding::turbopack::{
|
||||
core::{
|
||||
|
@ -80,10 +80,13 @@ pub async fn bootstrap(
|
|||
config: BootstrapConfigVc,
|
||||
) -> Result<EvaluatableAssetVc> {
|
||||
let path = asset.ident().path().await?;
|
||||
let path = base_path
|
||||
.await?
|
||||
.get_path_to(&path)
|
||||
.context("asset is not in base_path")?;
|
||||
let Some(path) = base_path.await?.get_path_to(&path) else {
|
||||
bail!(
|
||||
"asset {} is not in base path {}",
|
||||
asset.ident().to_string().await?,
|
||||
base_path.to_string().await?
|
||||
);
|
||||
};
|
||||
let path = if let Some((name, ext)) = path.rsplit_once('.') {
|
||||
if !ext.contains('/') {
|
||||
name
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod context;
|
||||
pub mod transition;
|
||||
pub mod page_transition;
|
||||
pub mod route_transition;
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
use anyhow::{bail, Result};
|
||||
use indexmap::indexmap;
|
||||
use turbo_tasks::Value;
|
||||
use turbopack_binding::{
|
||||
turbo::tasks_fs::FileSystemPathVc,
|
||||
turbopack::{
|
||||
core::{
|
||||
asset::AssetVc,
|
||||
chunk::{ChunkableAssetVc, ChunkingContextVc},
|
||||
compile_time_info::CompileTimeInfoVc,
|
||||
context::AssetContext,
|
||||
reference_type::{EcmaScriptModulesReferenceSubType, InnerAssetsVc, ReferenceType},
|
||||
source_asset::SourceAssetVc,
|
||||
},
|
||||
ecmascript::chunk_group_files_asset::ChunkGroupFilesAsset,
|
||||
turbopack::{
|
||||
module_options::ModuleOptionsContextVc,
|
||||
resolve_options_context::ResolveOptionsContextVc,
|
||||
transition::{Transition, TransitionVc},
|
||||
ModuleAssetContextVc,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::embed_js::next_js_file_path;
|
||||
|
||||
/// Transition into edge environment to render an app directory page.
|
||||
///
|
||||
/// It changes the environment to the provided edge environment, and wraps the
|
||||
/// process asset with the provided bootstrap_asset returning the chunks of all
|
||||
/// that for running them in the edge sandbox.
|
||||
#[turbo_tasks::value(shared)]
|
||||
pub struct NextEdgePageTransition {
|
||||
pub edge_compile_time_info: CompileTimeInfoVc,
|
||||
pub edge_chunking_context: ChunkingContextVc,
|
||||
pub edge_module_options_context: Option<ModuleOptionsContextVc>,
|
||||
pub edge_resolve_options_context: ResolveOptionsContextVc,
|
||||
pub output_path: FileSystemPathVc,
|
||||
pub bootstrap_asset: AssetVc,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl Transition for NextEdgePageTransition {
|
||||
#[turbo_tasks::function]
|
||||
fn process_compile_time_info(
|
||||
&self,
|
||||
_compile_time_info: CompileTimeInfoVc,
|
||||
) -> CompileTimeInfoVc {
|
||||
self.edge_compile_time_info
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn process_module_options_context(
|
||||
&self,
|
||||
context: ModuleOptionsContextVc,
|
||||
) -> ModuleOptionsContextVc {
|
||||
self.edge_module_options_context.unwrap_or(context)
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn process_resolve_options_context(
|
||||
&self,
|
||||
_context: ResolveOptionsContextVc,
|
||||
) -> ResolveOptionsContextVc {
|
||||
self.edge_resolve_options_context
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn process_module(
|
||||
&self,
|
||||
asset: AssetVc,
|
||||
context: ModuleAssetContextVc,
|
||||
) -> Result<AssetVc> {
|
||||
let asset = context.process(
|
||||
self.bootstrap_asset,
|
||||
Value::new(ReferenceType::Internal(InnerAssetsVc::cell(indexmap! {
|
||||
"APP_ENTRY".to_string() => asset,
|
||||
"APP_BOOTSTRAP".to_string() => context.with_transition("next-client").process(
|
||||
SourceAssetVc::new(next_js_file_path("entry/app/hydrate.tsx")).into(),
|
||||
Value::new(ReferenceType::EcmaScriptModules(
|
||||
EcmaScriptModulesReferenceSubType::Undefined,
|
||||
)),
|
||||
),
|
||||
}))),
|
||||
);
|
||||
|
||||
let Some(asset) = ChunkableAssetVc::resolve_from(asset).await? else {
|
||||
bail!("Internal module is not evaluatable");
|
||||
};
|
||||
|
||||
let asset = ChunkGroupFilesAsset {
|
||||
asset,
|
||||
client_root: self.output_path,
|
||||
chunking_context: self.edge_chunking_context,
|
||||
runtime_entries: None,
|
||||
};
|
||||
|
||||
Ok(asset.cell().into())
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ use turbopack_binding::{
|
|||
use crate::bootstrap::{route_bootstrap, BootstrapConfigVc};
|
||||
|
||||
#[turbo_tasks::value(shared)]
|
||||
pub struct NextEdgeTransition {
|
||||
pub struct NextEdgeRouteTransition {
|
||||
pub edge_compile_time_info: CompileTimeInfoVc,
|
||||
pub edge_chunking_context: ChunkingContextVc,
|
||||
pub edge_module_options_context: Option<ModuleOptionsContextVc>,
|
||||
|
@ -29,7 +29,7 @@ pub struct NextEdgeTransition {
|
|||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl Transition for NextEdgeTransition {
|
||||
impl Transition for NextEdgeRouteTransition {
|
||||
#[turbo_tasks::function]
|
||||
fn process_compile_time_info(
|
||||
&self,
|
|
@ -209,12 +209,6 @@ pub async fn get_next_server_import_map(
|
|||
)
|
||||
.await?;
|
||||
|
||||
import_map.insert_exact_alias(
|
||||
"@opentelemetry/api",
|
||||
// TODO(WEB-625) this actually need to prefer the local version of @opentelemetry/api
|
||||
ImportMapping::External(Some("next/dist/compiled/@opentelemetry/api".to_string())).into(),
|
||||
);
|
||||
|
||||
let ty = ty.into_value();
|
||||
|
||||
insert_next_server_special_aliases(&mut import_map, ty).await?;
|
||||
|
@ -357,6 +351,12 @@ pub async fn insert_next_server_special_aliases(
|
|||
) -> Result<()> {
|
||||
match ty {
|
||||
ServerContextType::Pages { pages_dir } => {
|
||||
import_map.insert_exact_alias(
|
||||
"@opentelemetry/api",
|
||||
// TODO(WEB-625) this actually need to prefer the local version of
|
||||
// @opentelemetry/api
|
||||
external_request_to_import_mapping("next/dist/compiled/@opentelemetry/api"),
|
||||
);
|
||||
insert_alias_to_alternatives(
|
||||
import_map,
|
||||
format!("{VIRTUAL_PACKAGE_NAME}/pages/_app"),
|
||||
|
@ -386,6 +386,12 @@ pub async fn insert_next_server_special_aliases(
|
|||
ServerContextType::AppSSR { app_dir }
|
||||
| ServerContextType::AppRSC { app_dir }
|
||||
| ServerContextType::AppRoute { app_dir } => {
|
||||
import_map.insert_exact_alias(
|
||||
"@opentelemetry/api",
|
||||
// TODO(WEB-625) this actually need to prefer the local version of
|
||||
// @opentelemetry/api
|
||||
request_to_import_mapping(app_dir, "next/dist/compiled/@opentelemetry/api"),
|
||||
);
|
||||
import_map.insert_exact_alias(
|
||||
"react",
|
||||
request_to_import_mapping(app_dir, "next/dist/compiled/react"),
|
||||
|
|
|
@ -62,7 +62,7 @@ use crate::{
|
|||
next_config::NextConfigVc,
|
||||
next_edge::{
|
||||
context::{get_edge_compile_time_info, get_edge_resolve_options_context},
|
||||
transition::NextEdgeTransition,
|
||||
route_transition::NextEdgeRouteTransition,
|
||||
},
|
||||
next_route_matcher::{
|
||||
NextExactMatcherVc, NextFallbackMatcherVc, NextParamsMatcherVc,
|
||||
|
@ -168,12 +168,12 @@ pub async fn create_page_source(
|
|||
),
|
||||
edge_compile_time_info.environment(),
|
||||
)
|
||||
.reference_chunk_source_maps(false)
|
||||
.reference_chunk_source_maps(should_debug("page_source"))
|
||||
.build();
|
||||
let edge_resolve_options_context =
|
||||
get_edge_resolve_options_context(project_root, server_ty, next_config, execution_context);
|
||||
|
||||
let next_edge_transition = NextEdgeTransition {
|
||||
let next_edge_transition = NextEdgeRouteTransition {
|
||||
edge_compile_time_info,
|
||||
edge_chunking_context,
|
||||
edge_module_options_context: None,
|
||||
|
@ -374,7 +374,7 @@ async fn create_page_source_for_file(
|
|||
),
|
||||
server_context.compile_time_info().environment(),
|
||||
)
|
||||
.reference_chunk_source_maps(false)
|
||||
.reference_chunk_source_maps(should_debug("page_source"))
|
||||
.build();
|
||||
|
||||
let data_node_path = node_path.join("data");
|
||||
|
@ -389,7 +389,7 @@ async fn create_page_source_for_file(
|
|||
),
|
||||
server_context.compile_time_info().environment(),
|
||||
)
|
||||
.reference_chunk_source_maps(false)
|
||||
.reference_chunk_source_maps(should_debug("page_source"))
|
||||
.build();
|
||||
|
||||
let client_chunking_context = get_client_chunking_context(
|
||||
|
@ -534,7 +534,7 @@ async fn create_not_found_page_source(
|
|||
),
|
||||
server_context.compile_time_info().environment(),
|
||||
)
|
||||
.reference_chunk_source_maps(false)
|
||||
.reference_chunk_source_maps(should_debug("page_source"))
|
||||
.build();
|
||||
|
||||
let client_chunking_context = get_client_chunking_context(
|
||||
|
|
|
@ -47,7 +47,7 @@ use crate::{
|
|||
next_config::NextConfigVc,
|
||||
next_edge::{
|
||||
context::{get_edge_compile_time_info, get_edge_resolve_options_context},
|
||||
transition::NextEdgeTransition,
|
||||
route_transition::NextEdgeRouteTransition,
|
||||
},
|
||||
next_import_map::get_next_build_import_map,
|
||||
next_server::context::{get_server_module_options_context, ServerContextType},
|
||||
|
@ -250,7 +250,7 @@ fn edge_transition_map(
|
|||
output_path.join("edge/assets"),
|
||||
edge_compile_time_info.environment(),
|
||||
)
|
||||
.reference_chunk_source_maps(false)
|
||||
.reference_chunk_source_maps(should_debug("router"))
|
||||
.build();
|
||||
|
||||
let edge_resolve_options_context = get_edge_resolve_options_context(
|
||||
|
@ -268,7 +268,7 @@ fn edge_transition_map(
|
|||
next_config,
|
||||
);
|
||||
|
||||
let next_edge_transition = NextEdgeTransition {
|
||||
let next_edge_transition = NextEdgeRouteTransition {
|
||||
edge_compile_time_info,
|
||||
edge_chunking_context,
|
||||
edge_module_options_context: Some(server_module_options_context),
|
||||
|
|
|
@ -92,6 +92,8 @@ lazy_static! {
|
|||
static ref DEBUG_BROWSER: bool = env::var("TURBOPACK_DEBUG_BROWSER").is_ok();
|
||||
/// Only starts the dev server on port 3000, but doesn't spawn a browser or run any tests.
|
||||
static ref DEBUG_START: bool = env::var("TURBOPACK_DEBUG_START").is_ok();
|
||||
/// When using TURBOPACK_DEBUG_START, this will open the browser to the dev server.
|
||||
static ref DEBUG_OPEN: bool = env::var("TURBOPACK_DEBUG_OPEN").is_ok();
|
||||
}
|
||||
|
||||
fn run_async_test<'a, T>(future: impl Future<Output = T> + Send + 'a) -> T {
|
||||
|
@ -137,19 +139,12 @@ fn test(resource: PathBuf) {
|
|||
run_result,
|
||||
} = run_async_test(run_test(resource));
|
||||
|
||||
if !uncaught_exceptions.is_empty() {
|
||||
panic!(
|
||||
"Uncaught exception(s) in test:\n{}",
|
||||
uncaught_exceptions.join("\n")
|
||||
)
|
||||
let mut messages = vec![];
|
||||
|
||||
if run_result.test_results.is_empty() {
|
||||
messages.push("No tests were run.".to_string());
|
||||
}
|
||||
|
||||
assert!(
|
||||
!run_result.test_results.is_empty(),
|
||||
"Expected one or more tests to run."
|
||||
);
|
||||
|
||||
let mut messages = vec![];
|
||||
for test_result in run_result.test_results {
|
||||
// It's possible to fail multiple tests across these tests,
|
||||
// so collect them and fail the respective test in Rust with
|
||||
|
@ -163,6 +158,10 @@ fn test(resource: PathBuf) {
|
|||
}
|
||||
}
|
||||
|
||||
for uncaught_exception in uncaught_exceptions {
|
||||
messages.push(format!("Uncaught exception: {}", uncaught_exception));
|
||||
}
|
||||
|
||||
if !messages.is_empty() {
|
||||
panic!(
|
||||
"Failed with error(s) in the following test(s):\n\n{}",
|
||||
|
@ -257,7 +256,11 @@ async fn run_test(resource: PathBuf) -> JsResult {
|
|||
let test_dir = resource_temp.to_path_buf();
|
||||
let workspace_root = cargo_workspace_root.parent().unwrap().parent().unwrap();
|
||||
let project_dir = test_dir.join("input");
|
||||
let requested_addr = get_free_local_addr().unwrap();
|
||||
let requested_addr = if *DEBUG_START {
|
||||
"127.0.0.1:3000".parse().unwrap()
|
||||
} else {
|
||||
get_free_local_addr().unwrap()
|
||||
};
|
||||
|
||||
let mock_dir = resource_temp.join("__httpmock__");
|
||||
let mock_server_future = get_mock_server_future(&mock_dir);
|
||||
|
@ -265,89 +268,95 @@ async fn run_test(resource: PathBuf) -> JsResult {
|
|||
let (issue_tx, mut issue_rx) = unbounded_channel();
|
||||
let issue_tx = TransientInstance::new(issue_tx);
|
||||
|
||||
let tt = TurboTasks::new(MemoryBackend::default());
|
||||
let server = NextDevServerBuilder::new(
|
||||
tt.clone(),
|
||||
project_dir.to_string_lossy().to_string(),
|
||||
workspace_root.to_string_lossy().to_string(),
|
||||
)
|
||||
.entry_request(EntryRequest::Module(
|
||||
"@turbo/pack-test-harness".to_string(),
|
||||
"/harness".to_string(),
|
||||
))
|
||||
.entry_request(EntryRequest::Relative("index.js".to_owned()))
|
||||
.eager_compile(false)
|
||||
.hostname(requested_addr.ip())
|
||||
.port(requested_addr.port())
|
||||
.log_level(turbopack_binding::turbopack::core::issue::IssueSeverity::Warning)
|
||||
.log_detail(true)
|
||||
.issue_reporter(Box::new(move || {
|
||||
TestIssueReporterVc::new(issue_tx.clone()).into()
|
||||
}))
|
||||
.show_all(true)
|
||||
.build()
|
||||
.await
|
||||
.unwrap();
|
||||
let result;
|
||||
|
||||
let local_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), server.addr.port());
|
||||
|
||||
println!(
|
||||
"{event_type} - server started at http://{address}",
|
||||
event_type = "ready".green(),
|
||||
address = server.addr
|
||||
);
|
||||
|
||||
if *DEBUG_START {
|
||||
webbrowser::open(&local_addr.to_string()).unwrap();
|
||||
tokio::select! {
|
||||
_ = mock_server_future => {},
|
||||
_ = pending() => {},
|
||||
_ = server.future => {},
|
||||
};
|
||||
panic!("Never resolves")
|
||||
}
|
||||
|
||||
let result = tokio::select! {
|
||||
// Poll the mock_server first to add the env var
|
||||
_ = mock_server_future => panic!("Never resolves"),
|
||||
r = run_browser(local_addr, &project_dir) => r.expect("error while running browser"),
|
||||
_ = server.future => panic!("Never resolves"),
|
||||
};
|
||||
|
||||
env::remove_var("TURBOPACK_TEST_ONLY_MOCK_SERVER");
|
||||
|
||||
let task = tt.spawn_once_task(async move {
|
||||
let issues_fs = DiskFileSystemVc::new(
|
||||
"issues".to_string(),
|
||||
resource.join("issues").to_string_lossy().to_string(),
|
||||
{
|
||||
let tt = TurboTasks::new(MemoryBackend::default());
|
||||
let server = NextDevServerBuilder::new(
|
||||
tt.clone(),
|
||||
project_dir.to_string_lossy().to_string(),
|
||||
workspace_root.to_string_lossy().to_string(),
|
||||
)
|
||||
.as_file_system();
|
||||
.entry_request(EntryRequest::Module(
|
||||
"@turbo/pack-test-harness".to_string(),
|
||||
"/harness".to_string(),
|
||||
))
|
||||
.entry_request(EntryRequest::Relative("index.js".to_owned()))
|
||||
.eager_compile(false)
|
||||
.hostname(requested_addr.ip())
|
||||
.port(requested_addr.port())
|
||||
.log_level(turbopack_binding::turbopack::core::issue::IssueSeverity::Warning)
|
||||
.log_detail(true)
|
||||
.issue_reporter(Box::new(move || {
|
||||
TestIssueReporterVc::new(issue_tx.clone()).into()
|
||||
}))
|
||||
.show_all(true)
|
||||
.build()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut issues = vec![];
|
||||
while let Ok(issue) = issue_rx.try_recv() {
|
||||
issues.push(issue);
|
||||
let local_addr =
|
||||
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), server.addr.port());
|
||||
|
||||
println!(
|
||||
"{event_type} - server started at http://{address}",
|
||||
event_type = "ready".green(),
|
||||
address = server.addr
|
||||
);
|
||||
|
||||
if *DEBUG_START {
|
||||
if *DEBUG_OPEN {
|
||||
webbrowser::open(&format!("http://{}", local_addr)).unwrap();
|
||||
}
|
||||
tokio::select! {
|
||||
_ = mock_server_future => {},
|
||||
_ = pending() => {},
|
||||
_ = server.future => {},
|
||||
};
|
||||
panic!("Never resolves")
|
||||
}
|
||||
|
||||
snapshot_issues(
|
||||
issues.iter().cloned(),
|
||||
issues_fs.root(),
|
||||
&cargo_workspace_root.to_string_lossy(),
|
||||
)
|
||||
.await?;
|
||||
result = tokio::select! {
|
||||
// Poll the mock_server first to add the env var
|
||||
_ = mock_server_future => panic!("Never resolves"),
|
||||
r = run_browser(local_addr, &project_dir) => r.expect("error while running browser"),
|
||||
_ = server.future => panic!("Never resolves"),
|
||||
};
|
||||
|
||||
Ok(NothingVc::new().into())
|
||||
});
|
||||
tt.wait_task_completion(task, true).await.unwrap();
|
||||
env::remove_var("TURBOPACK_TEST_ONLY_MOCK_SERVER");
|
||||
|
||||
// This sometimes fails for the following test:
|
||||
// test_tests__integration__next__webpack_loaders__no_options__input
|
||||
retry(
|
||||
let task = tt.spawn_once_task(async move {
|
||||
let issues_fs = DiskFileSystemVc::new(
|
||||
"issues".to_string(),
|
||||
resource.join("issues").to_string_lossy().to_string(),
|
||||
)
|
||||
.as_file_system();
|
||||
|
||||
let mut issues = vec![];
|
||||
while let Ok(issue) = issue_rx.try_recv() {
|
||||
issues.push(issue);
|
||||
}
|
||||
|
||||
snapshot_issues(
|
||||
issues.iter().cloned(),
|
||||
issues_fs.root(),
|
||||
&cargo_workspace_root.to_string_lossy(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(NothingVc::new().into())
|
||||
});
|
||||
tt.wait_task_completion(task, true).await.unwrap();
|
||||
}
|
||||
|
||||
if let Err(err) = retry(
|
||||
(),
|
||||
|()| std::fs::remove_dir_all(&resource_temp),
|
||||
3,
|
||||
Duration::from_millis(100),
|
||||
)
|
||||
.expect("failed to remove temporary directory");
|
||||
) {
|
||||
eprintln!("Failed to remove temporary directory: {}", err);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
|
|
@ -10,7 +10,11 @@ async function getJson(url) {
|
|||
const res = await fetch(url)
|
||||
const text = await res.text()
|
||||
const jsonText = /(\{[^}]*\})/.exec(text)
|
||||
return JSON.parse(jsonText[0].replace(/"/g, '"'))
|
||||
try {
|
||||
return JSON.parse(jsonText[0].replace(/"/g, '"'))
|
||||
} catch (err) {
|
||||
throw new Error(`Expected JSON but got:\n${text}`)
|
||||
}
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
|
@ -63,17 +67,10 @@ function runTests() {
|
|||
|
||||
it('app with edge runtime should import edge conditions', async () => {
|
||||
const json = await getJson('/app-edge')
|
||||
// TODO We don't currently support edge config in app rendering.
|
||||
// When we do, this needs to be updated.
|
||||
expect(json).not.toMatchObject({
|
||||
expect(json).toMatchObject({
|
||||
edgeThenNode: 'edge',
|
||||
nodeThenEdge: 'edge',
|
||||
})
|
||||
// TODO: delete this.
|
||||
expect(json).toMatchObject({
|
||||
edgeThenNode: 'node',
|
||||
nodeThenEdge: 'node',
|
||||
})
|
||||
})
|
||||
|
||||
it('app route with nodejs runtime should import node conditions', async () => {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
PlainIssue {
|
||||
severity: Error,
|
||||
context: "[project]/packages/next/dist/compiled/nanoid/index.cjs",
|
||||
category: "resolve",
|
||||
title: "Error resolving commonjs request",
|
||||
description: "unable to resolve module \"crypto\"",
|
||||
detail: "It was not possible to find the requested file.\nParsed request as written in source code: module \"crypto\"\nPath where resolving has started: [project]/packages/next/dist/compiled/nanoid/index.cjs\nType of request: commonjs request\nImport map: No import map entry\n",
|
||||
documentation_link: "",
|
||||
source: Some(
|
||||
PlainIssueSource {
|
||||
asset: PlainAsset {
|
||||
ident: "[project]/packages/next/dist/compiled/nanoid/index.cjs",
|
||||
},
|
||||
start: SourcePos {
|
||||
line: 0,
|
||||
column: 45,
|
||||
},
|
||||
end: SourcePos {
|
||||
line: 0,
|
||||
column: 62,
|
||||
},
|
||||
},
|
||||
),
|
||||
sub_issues: [],
|
||||
processing_path: Some(
|
||||
[
|
||||
PlainIssueProcessingPathItem {
|
||||
context: Some(
|
||||
"[project]/packages/next-swc/crates/next-dev-tests/tests/temp/next/import/conditions/input/app",
|
||||
),
|
||||
description: "Next.js App Page Route /app-edge",
|
||||
},
|
||||
],
|
||||
),
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
PlainIssue {
|
||||
severity: Error,
|
||||
context: "[project]/packages/next/dist/compiled/nanoid/index.cjs",
|
||||
category: "resolve",
|
||||
title: "Error resolving commonjs request",
|
||||
description: "unable to resolve module \"crypto\"",
|
||||
detail: "It was not possible to find the requested file.\nParsed request as written in source code: module \"crypto\"\nPath where resolving has started: [project]/packages/next/dist/compiled/nanoid/index.cjs\nType of request: commonjs request\nImport map: No import map entry\n",
|
||||
documentation_link: "",
|
||||
source: Some(
|
||||
PlainIssueSource {
|
||||
asset: PlainAsset {
|
||||
ident: "[project]/packages/next/dist/compiled/nanoid/index.cjs",
|
||||
},
|
||||
start: SourcePos {
|
||||
line: 0,
|
||||
column: 45,
|
||||
},
|
||||
end: SourcePos {
|
||||
line: 0,
|
||||
column: 62,
|
||||
},
|
||||
},
|
||||
),
|
||||
sub_issues: [],
|
||||
processing_path: Some(
|
||||
[
|
||||
PlainIssueProcessingPathItem {
|
||||
context: Some(
|
||||
"[project]/packages/next-swc/crates/next-dev-tests/tests/temp/next/import/conditions/input/app",
|
||||
),
|
||||
description: "Next.js App Page Route /app-edge",
|
||||
},
|
||||
PlainIssueProcessingPathItem {
|
||||
context: Some(
|
||||
"[next]/entry/app-edge-renderer.tsx",
|
||||
),
|
||||
description: "server-side rendering /app-edge",
|
||||
},
|
||||
],
|
||||
),
|
||||
}
|
|
@ -623,33 +623,22 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
|
|||
}
|
||||
}
|
||||
|
||||
// Prefer to modify next/src/server/app-render/entry-base.ts since this is shared with Turbopack.
|
||||
// Any changes to this code should be reflected in Turbopack's app_source.rs and/or app-renderer.tsx as well.
|
||||
const result = `
|
||||
export ${treeCodeResult.treeCode}
|
||||
export ${treeCodeResult.pages}
|
||||
|
||||
export { default as AppRouter } from 'next/dist/client/components/app-router'
|
||||
export { default as LayoutRouter } from 'next/dist/client/components/layout-router'
|
||||
export { default as RenderFromTemplateContext } from 'next/dist/client/components/render-from-template-context'
|
||||
export { default as GlobalError } from ${JSON.stringify(
|
||||
treeCodeResult.globalError || 'next/dist/client/components/error-boundary'
|
||||
)}
|
||||
export const originalPathname = ${JSON.stringify(page)}
|
||||
export const __next_app__ = {
|
||||
require: __webpack_require__,
|
||||
// all modules are in the entry chunk, so we never actually need to load chunks in webpack
|
||||
loadChunk: () => Promise.resolve()
|
||||
}
|
||||
|
||||
export { staticGenerationAsyncStorage } from 'next/dist/client/components/static-generation-async-storage'
|
||||
|
||||
export { requestAsyncStorage } from 'next/dist/client/components/request-async-storage'
|
||||
export { actionAsyncStorage } from 'next/dist/client/components/action-async-storage'
|
||||
|
||||
export { staticGenerationBailout } from 'next/dist/client/components/static-generation-bailout'
|
||||
export { default as StaticGenerationSearchParamsBailoutProvider } from 'next/dist/client/components/static-generation-searchparams-bailout-provider'
|
||||
export { createSearchParamsBailoutProxy } from 'next/dist/client/components/searchparams-bailout-proxy'
|
||||
|
||||
export * as serverHooks from 'next/dist/client/components/hooks-server-context'
|
||||
|
||||
export { renderToReadableStream, decodeReply, decodeAction } from 'react-server-dom-webpack/server.edge'
|
||||
export const __next_app_webpack_require__ = __webpack_require__
|
||||
export { preloadStyle, preloadFont, preconnect } from 'next/dist/server/app-render/rsc/preloads'
|
||||
|
||||
export const originalPathname = "${page}"
|
||||
export * from 'next/dist/server/app-render/entry-base'
|
||||
`
|
||||
|
||||
return result
|
||||
|
|
|
@ -98,7 +98,7 @@ export function getRender({
|
|||
getServerSideProps: pageMod.getServerSideProps,
|
||||
getStaticPaths: pageMod.getStaticPaths,
|
||||
ComponentMod: pageMod,
|
||||
isAppPath: !!pageMod.__next_app_webpack_require__,
|
||||
isAppPath: !!pageMod.__next_app__,
|
||||
pathname,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -400,7 +400,7 @@ export async function handleAction({
|
|||
process.env.NEXT_RUNTIME === 'edge' ? 'edge' : 'node'
|
||||
][actionId].workers[workerName]
|
||||
const actionHandler =
|
||||
ComponentMod.__next_app_webpack_require__(actionModId)[actionId]
|
||||
ComponentMod.__next_app__.require(actionModId)[actionId]
|
||||
|
||||
const returnVal = await actionHandler.apply(null, bound)
|
||||
|
||||
|
|
|
@ -13,7 +13,10 @@ export function createServerComponentRenderer<Props>(
|
|||
ComponentToRender: (props: Props) => any,
|
||||
ComponentMod: {
|
||||
renderToReadableStream: any
|
||||
__next_app_webpack_require__?: any
|
||||
__next_app__?: {
|
||||
require: any
|
||||
loadChunk: any
|
||||
}
|
||||
},
|
||||
{
|
||||
transformStream,
|
||||
|
@ -31,14 +34,14 @@ export function createServerComponentRenderer<Props>(
|
|||
serverComponentsErrorHandler: ReturnType<typeof createErrorHandler>,
|
||||
nonce?: string
|
||||
): (props: Props) => JSX.Element {
|
||||
// We need to expose the `__webpack_require__` API globally for
|
||||
// We need to expose the bundled `require` API globally for
|
||||
// react-server-dom-webpack. This is a hack until we find a better way.
|
||||
if (ComponentMod.__next_app_webpack_require__) {
|
||||
if (ComponentMod.__next_app__) {
|
||||
// @ts-ignore
|
||||
globalThis.__next_require__ = ComponentMod.__next_app_webpack_require__
|
||||
globalThis.__next_require__ = ComponentMod.__next_app__.require
|
||||
|
||||
// @ts-ignore
|
||||
globalThis.__next_chunk_load__ = () => Promise.resolve()
|
||||
globalThis.__next_chunk_load__ = ComponentMod.__next_app__.loadChunk
|
||||
}
|
||||
|
||||
let RSCStream: ReadableStream<Uint8Array>
|
||||
|
|
52
packages/next/src/server/app-render/entry-base.ts
Normal file
52
packages/next/src/server/app-render/entry-base.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
const { default: AppRouter } =
|
||||
require('next/dist/client/components/app-router') as typeof import('../../client/components/app-router')
|
||||
const { default: LayoutRouter } =
|
||||
require('next/dist/client/components/layout-router') as typeof import('../../client/components/layout-router')
|
||||
const { default: RenderFromTemplateContext } =
|
||||
require('next/dist/client/components/render-from-template-context') as typeof import('../../client/components/render-from-template-context')
|
||||
|
||||
const { staticGenerationAsyncStorage } =
|
||||
require('next/dist/client/components/static-generation-async-storage') as typeof import('../../client/components/static-generation-async-storage')
|
||||
|
||||
const { requestAsyncStorage } =
|
||||
require('next/dist/client/components/request-async-storage') as typeof import('../../client/components/request-async-storage')
|
||||
const { actionAsyncStorage } =
|
||||
require('next/dist/client/components/action-async-storage') as typeof import('../../client/components/action-async-storage')
|
||||
|
||||
const { staticGenerationBailout } =
|
||||
require('next/dist/client/components/static-generation-bailout') as typeof import('../../client/components/static-generation-bailout')
|
||||
const { default: StaticGenerationSearchParamsBailoutProvider } =
|
||||
require('next/dist/client/components/static-generation-searchparams-bailout-provider') as typeof import('../../client/components/static-generation-searchparams-bailout-provider')
|
||||
const { createSearchParamsBailoutProxy } =
|
||||
require('next/dist/client/components/searchparams-bailout-proxy') as typeof import('../../client/components/searchparams-bailout-proxy')
|
||||
|
||||
const serverHooks =
|
||||
require('next/dist/client/components/hooks-server-context') as typeof import('../../client/components/hooks-server-context')
|
||||
|
||||
const {
|
||||
renderToReadableStream,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
} = require('react-server-dom-webpack/server.edge')
|
||||
const { preloadStyle, preloadFont, preconnect } =
|
||||
require('next/dist/server/app-render/rsc/preloads') as typeof import('../../server/app-render/rsc/preloads')
|
||||
|
||||
export {
|
||||
AppRouter,
|
||||
LayoutRouter,
|
||||
RenderFromTemplateContext,
|
||||
staticGenerationAsyncStorage,
|
||||
requestAsyncStorage,
|
||||
actionAsyncStorage,
|
||||
staticGenerationBailout,
|
||||
createSearchParamsBailoutProxy,
|
||||
serverHooks,
|
||||
renderToReadableStream,
|
||||
decodeReply,
|
||||
decodeAction,
|
||||
preloadStyle,
|
||||
preloadFont,
|
||||
preconnect,
|
||||
StaticGenerationSearchParamsBailoutProvider,
|
||||
}
|
|
@ -1,6 +1,3 @@
|
|||
import type { ServerResponse } from 'http'
|
||||
import { Writable } from 'stream'
|
||||
|
||||
type ContentTypeOption = string | undefined
|
||||
|
||||
export type RenderResultMetadata = {
|
||||
|
@ -14,6 +11,13 @@ export type RenderResultMetadata = {
|
|||
|
||||
type RenderResultResponse = string | ReadableStream<Uint8Array> | null
|
||||
|
||||
export interface PipeTarget {
|
||||
write: (chunk: Uint8Array) => unknown
|
||||
end: () => unknown
|
||||
flush?: () => unknown
|
||||
destroy: (err?: Error) => unknown
|
||||
}
|
||||
|
||||
export default class RenderResult {
|
||||
/**
|
||||
* The detected content type for the response. This is used to set the
|
||||
|
@ -91,7 +95,7 @@ export default class RenderResult {
|
|||
return this.response
|
||||
}
|
||||
|
||||
public async pipe(res: ServerResponse | Writable): Promise<void> {
|
||||
public async pipe(res: PipeTarget): Promise<void> {
|
||||
if (this.response === null) {
|
||||
throw new Error('Invariant: response is null. This is a bug in Next.js')
|
||||
}
|
||||
|
|
|
@ -984,8 +984,8 @@ importers:
|
|||
'@types/react': 18.2.7
|
||||
'@types/react-dom': 18.2.4
|
||||
'@vercel/ncc': ^0.36.0
|
||||
'@vercel/turbopack-ecmascript-runtime': https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230615.1
|
||||
'@vercel/turbopack-node': https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-node/js?turbopack-230615.1
|
||||
'@vercel/turbopack-ecmascript-runtime': https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230622.2
|
||||
'@vercel/turbopack-node': https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-node/js?turbopack-230622.2
|
||||
anser: ^2.1.1
|
||||
css.escape: ^1.5.1
|
||||
find-up: ^6.3.0
|
||||
|
@ -997,8 +997,8 @@ importers:
|
|||
stacktrace-parser: ^0.1.10
|
||||
strip-ansi: ^7.0.1
|
||||
dependencies:
|
||||
'@vercel/turbopack-ecmascript-runtime': '@gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230615.1_react-refresh@0.12.0'
|
||||
'@vercel/turbopack-node': '@gitpkg.vercel.app/vercel/turbo/crates/turbopack-node/js?turbopack-230615.1'
|
||||
'@vercel/turbopack-ecmascript-runtime': '@gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230622.2_react-refresh@0.12.0'
|
||||
'@vercel/turbopack-node': '@gitpkg.vercel.app/vercel/turbo/crates/turbopack-node/js?turbopack-230622.2'
|
||||
anser: 2.1.1
|
||||
css.escape: 1.5.1
|
||||
next: link:../../../../next
|
||||
|
@ -6116,7 +6116,7 @@ packages:
|
|||
dependencies:
|
||||
'@mdx-js/mdx': 2.2.1
|
||||
source-map: 0.7.3
|
||||
webpack: 5.86.0
|
||||
webpack: 5.86.0_@swc+core@1.3.55
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
@ -6790,7 +6790,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@swc/core-darwin-x64/1.3.55:
|
||||
|
@ -6799,7 +6798,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@swc/core-linux-arm-gnueabihf/1.3.55:
|
||||
|
@ -6808,7 +6806,6 @@ packages:
|
|||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@swc/core-linux-arm64-gnu/1.3.55:
|
||||
|
@ -6817,7 +6814,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@swc/core-linux-arm64-musl/1.3.55:
|
||||
|
@ -6826,7 +6822,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@swc/core-linux-x64-gnu/1.3.55:
|
||||
|
@ -6835,7 +6830,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@swc/core-linux-x64-musl/1.3.55:
|
||||
|
@ -6844,7 +6838,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@swc/core-win32-arm64-msvc/1.3.55:
|
||||
|
@ -6853,7 +6846,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@swc/core-win32-ia32-msvc/1.3.55:
|
||||
|
@ -6862,7 +6854,6 @@ packages:
|
|||
cpu: [ia32]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@swc/core-win32-x64-msvc/1.3.55:
|
||||
|
@ -6871,7 +6862,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@swc/core/1.3.55_@swc+helpers@0.5.1:
|
||||
|
@ -6896,7 +6886,6 @@ packages:
|
|||
'@swc/core-win32-arm64-msvc': 1.3.55
|
||||
'@swc/core-win32-ia32-msvc': 1.3.55
|
||||
'@swc/core-win32-x64-msvc': 1.3.55
|
||||
dev: true
|
||||
|
||||
/@swc/helpers/0.4.14:
|
||||
resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==}
|
||||
|
@ -23799,7 +23788,6 @@ packages:
|
|||
serialize-javascript: 6.0.1
|
||||
terser: 5.17.7
|
||||
webpack: 5.86.0_@swc+core@1.3.55
|
||||
dev: true
|
||||
|
||||
/terser/5.10.0:
|
||||
resolution: {integrity: sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==}
|
||||
|
@ -25157,7 +25145,6 @@ packages:
|
|||
- '@swc/core'
|
||||
- esbuild
|
||||
- uglify-js
|
||||
dev: true
|
||||
|
||||
/websocket-driver/0.7.3:
|
||||
resolution: {integrity: sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==}
|
||||
|
@ -25566,9 +25553,9 @@ packages:
|
|||
/zwitch/2.0.4:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
|
||||
'@gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230615.1_react-refresh@0.12.0':
|
||||
resolution: {tarball: https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230615.1}
|
||||
id: '@gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230615.1'
|
||||
'@gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230622.2_react-refresh@0.12.0':
|
||||
resolution: {tarball: https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230622.2}
|
||||
id: '@gitpkg.vercel.app/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230622.2'
|
||||
name: '@vercel/turbopack-ecmascript-runtime'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
|
@ -25579,8 +25566,8 @@ packages:
|
|||
- webpack
|
||||
dev: false
|
||||
|
||||
'@gitpkg.vercel.app/vercel/turbo/crates/turbopack-node/js?turbopack-230615.1':
|
||||
resolution: {tarball: https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-node/js?turbopack-230615.1}
|
||||
'@gitpkg.vercel.app/vercel/turbo/crates/turbopack-node/js?turbopack-230622.2':
|
||||
resolution: {tarball: https://gitpkg.vercel.app/vercel/turbo/crates/turbopack-node/js?turbopack-230622.2}
|
||||
name: '@vercel/turbopack-node'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
|
|
Loading…
Reference in a new issue