Turobpack: Next.rs API (part 1) (#52259)
### What? Creates a NAPI api for Next.rs to be used in Next.js ### Why? ### How? Co-authored-by: Alex Kirszenberg <1621758+alexkirsz@users.noreply.github.com>
This commit is contained in:
parent
88084e6b7a
commit
ca1129c463
32 changed files with 2206 additions and 112 deletions
88
Cargo.lock
generated
88
Cargo.lock
generated
|
@ -412,7 +412,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "auto-hash-map"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
@ -3367,6 +3367,23 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "next-api"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
"indexmap",
|
||||
"next-core",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"turbo-tasks",
|
||||
"turbopack-binding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "next-build"
|
||||
version = "0.1.0"
|
||||
|
@ -3519,6 +3536,7 @@ dependencies = [
|
|||
"napi",
|
||||
"napi-build",
|
||||
"napi-derive",
|
||||
"next-api",
|
||||
"next-build",
|
||||
"next-core",
|
||||
"next-dev",
|
||||
|
@ -3581,7 +3599,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "node-file-trace"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
|
@ -7241,7 +7259,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto-hash-map",
|
||||
|
@ -7272,7 +7290,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-build"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo-lock",
|
||||
|
@ -7284,7 +7302,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-bytes"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
|
@ -7299,7 +7317,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-env"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dotenvs",
|
||||
|
@ -7313,7 +7331,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-fetch"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
|
@ -7330,7 +7348,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-fs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto-hash-map",
|
||||
|
@ -7360,7 +7378,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-hash"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"base16",
|
||||
"hex",
|
||||
|
@ -7372,7 +7390,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
|
@ -7386,7 +7404,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-macros-shared"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -7396,7 +7414,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-malloc"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"mimalloc",
|
||||
]
|
||||
|
@ -7404,7 +7422,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-memory"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto-hash-map",
|
||||
|
@ -7427,7 +7445,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbo-tasks-testing"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto-hash-map",
|
||||
|
@ -7440,7 +7458,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
|
@ -7470,7 +7488,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-bench"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chromiumoxide",
|
||||
|
@ -7500,7 +7518,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-binding"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"auto-hash-map",
|
||||
"mdxjs",
|
||||
|
@ -7542,7 +7560,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-build"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
|
@ -7562,7 +7580,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-cli-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.11",
|
||||
|
@ -7586,7 +7604,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-core"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -7614,7 +7632,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-create-test-app"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.1.11",
|
||||
|
@ -7627,7 +7645,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-css"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -7649,7 +7667,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-dev"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
|
@ -7673,7 +7691,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-dev-server"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
|
@ -7709,7 +7727,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-ecmascript"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -7742,7 +7760,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-ecmascript-plugins"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -7765,7 +7783,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-ecmascript-runtime"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indoc",
|
||||
|
@ -7782,7 +7800,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-env"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
|
@ -7798,7 +7816,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-image"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.0",
|
||||
|
@ -7818,7 +7836,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-json"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
|
@ -7833,7 +7851,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-mdx"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mdxjs",
|
||||
|
@ -7848,7 +7866,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-node"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
|
@ -7883,7 +7901,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-static"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
|
@ -7899,7 +7917,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-swc-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"swc_core",
|
||||
"turbo-tasks",
|
||||
|
@ -7910,7 +7928,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "turbopack-test-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.2#4022f2b0e5bf6183b813f3ff32267d24f058cc82"
|
||||
source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230713.3#e3c68fac81352b24bfcfa1f14b56c0ef0a391917"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"once_cell",
|
||||
|
|
|
@ -5,6 +5,7 @@ members = [
|
|||
"packages/next-swc/crates/core",
|
||||
"packages/next-swc/crates/napi",
|
||||
"packages/next-swc/crates/wasm",
|
||||
"packages/next-swc/crates/next-api",
|
||||
"packages/next-swc/crates/next-build",
|
||||
"packages/next-swc/crates/next-core",
|
||||
"packages/next-swc/crates/next-dev",
|
||||
|
@ -26,6 +27,7 @@ lto = true
|
|||
|
||||
[workspace.dependencies]
|
||||
# Workspace crates
|
||||
next-api = { path = "packages/next-swc/crates/next-api", default-features = false }
|
||||
next-build = { path = "packages/next-swc/crates/next-build", default-features = false }
|
||||
next-core = { path = "packages/next-swc/crates/next-core", default-features = false }
|
||||
next-dev = { path = "packages/next-swc/crates/next-dev", default-features = false, features = [
|
||||
|
@ -42,11 +44,11 @@ swc_core = { version = "0.79.13" }
|
|||
testing = { version = "0.33.20" }
|
||||
|
||||
# Turbo crates
|
||||
turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230713.2" }
|
||||
turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230713.3" }
|
||||
# [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-230713.2" }
|
||||
turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230713.3" }
|
||||
# [TODO]: need to refactor embed_directory! macro usage in next-core
|
||||
turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230713.2" }
|
||||
turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230713.3" }
|
||||
|
||||
# General Deps
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ napi = { version = "2", default-features = false, features = [
|
|||
napi-derive = "2"
|
||||
next-swc = { version = "0.0.0", path = "../core" }
|
||||
next-dev = { workspace = true }
|
||||
next-api = { workspace = true }
|
||||
next-build = { workspace = true }
|
||||
next-core = { workspace = true }
|
||||
turbo-tasks = { workspace = true }
|
||||
|
|
|
@ -49,6 +49,7 @@ use turbopack_binding::swc::core::{
|
|||
pub mod app_structure;
|
||||
pub mod mdx;
|
||||
pub mod minify;
|
||||
pub mod next_api;
|
||||
pub mod parse;
|
||||
pub mod transform;
|
||||
pub mod turbopack;
|
||||
|
@ -116,6 +117,7 @@ static REGISTER_ONCE: Once = Once::new();
|
|||
|
||||
fn register() {
|
||||
REGISTER_ONCE.call_once(|| {
|
||||
::next_api::register();
|
||||
next_core::register();
|
||||
include!(concat!(env!("OUT_DIR"), "/register.rs"));
|
||||
});
|
||||
|
|
57
packages/next-swc/crates/napi/src/next_api/endpoint.rs
Normal file
57
packages/next-swc/crates/napi/src/next_api/endpoint.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use napi::{bindgen_prelude::External, JsFunction};
|
||||
use next_api::route::{Endpoint, EndpointVc, WrittenEndpoint};
|
||||
use turbopack_binding::turbopack::core::error::PrettyPrintError;
|
||||
|
||||
use super::utils::{subscribe, NapiDiagnostic, NapiIssue, RootTask, VcArc};
|
||||
|
||||
#[napi(object)]
|
||||
pub struct NapiWrittenEndpoint {
|
||||
pub server_entry_path: String,
|
||||
pub server_paths: Vec<String>,
|
||||
pub issues: Vec<NapiIssue>,
|
||||
pub diagnostics: Vec<NapiDiagnostic>,
|
||||
}
|
||||
|
||||
impl From<&WrittenEndpoint> for NapiWrittenEndpoint {
|
||||
fn from(written_endpoint: &WrittenEndpoint) -> Self {
|
||||
Self {
|
||||
server_entry_path: written_endpoint.server_entry_path.clone(),
|
||||
server_paths: written_endpoint.server_paths.clone(),
|
||||
issues: vec![],
|
||||
diagnostics: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub async fn endpoint_write_to_disk(
|
||||
#[napi(ts_arg_type = "{ __napiType: \"Endpoint\" }")] endpoint: External<VcArc<EndpointVc>>,
|
||||
) -> napi::Result<NapiWrittenEndpoint> {
|
||||
let turbo_tasks = endpoint.turbo_tasks().clone();
|
||||
let endpoint = **endpoint;
|
||||
let written = turbo_tasks
|
||||
.run_once(async move { endpoint.write_to_disk().strongly_consistent().await })
|
||||
.await
|
||||
.map_err(|e| napi::Error::from_reason(PrettyPrintError(&e).to_string()))?;
|
||||
// TODO peek_issues and diagnostics
|
||||
Ok((&*written).into())
|
||||
}
|
||||
|
||||
#[napi(ts_return_type = "{ __napiType: \"RootTask\" }")]
|
||||
pub fn endpoint_changed_subscribe(
|
||||
#[napi(ts_arg_type = "{ __napiType: \"Endpoint\" }")] endpoint: External<VcArc<EndpointVc>>,
|
||||
func: JsFunction,
|
||||
) -> napi::Result<External<RootTask>> {
|
||||
let turbo_tasks = endpoint.turbo_tasks().clone();
|
||||
let endpoint = **endpoint;
|
||||
subscribe(
|
||||
turbo_tasks,
|
||||
func,
|
||||
move || async move {
|
||||
endpoint.changed().await?;
|
||||
// TODO peek_issues and diagnostics
|
||||
Ok(())
|
||||
},
|
||||
|_ctx| Ok(vec![()]),
|
||||
)
|
||||
}
|
3
packages/next-swc/crates/napi/src/next_api/mod.rs
Normal file
3
packages/next-swc/crates/napi/src/next_api/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod endpoint;
|
||||
pub mod project;
|
||||
pub mod utils;
|
198
packages/next-swc/crates/napi/src/next_api/project.rs
Normal file
198
packages/next-swc/crates/napi/src/next_api/project.rs
Normal file
|
@ -0,0 +1,198 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use napi::{bindgen_prelude::External, JsFunction};
|
||||
use next_api::{
|
||||
project::{Middleware, ProjectOptions, ProjectVc},
|
||||
route::{EndpointVc, Route},
|
||||
};
|
||||
use turbo_tasks::TurboTasks;
|
||||
use turbopack_binding::{
|
||||
turbo::tasks_memory::MemoryBackend, turbopack::core::error::PrettyPrintError,
|
||||
};
|
||||
|
||||
use super::utils::{serde_enum_to_string, subscribe, NapiDiagnostic, NapiIssue, RootTask, VcArc};
|
||||
use crate::register;
|
||||
|
||||
#[napi(object)]
|
||||
pub struct NapiProjectOptions {
|
||||
/// A root path from which all files must be nested under. Trying to access
|
||||
/// a file outside this root will fail. Think of this as a chroot.
|
||||
pub root_path: String,
|
||||
|
||||
/// A path inside the root_path which contains the app/pages directories.
|
||||
pub project_path: String,
|
||||
|
||||
/// Whether to watch he filesystem for file changes.
|
||||
pub watch: bool,
|
||||
|
||||
/// The contents of next.config.js, serialized to JSON.
|
||||
pub next_config: String,
|
||||
|
||||
/// An upper bound of memory that turbopack will attempt to stay under.
|
||||
pub memory_limit: Option<f64>,
|
||||
}
|
||||
|
||||
impl From<NapiProjectOptions> for ProjectOptions {
|
||||
fn from(val: NapiProjectOptions) -> Self {
|
||||
ProjectOptions {
|
||||
root_path: val.root_path,
|
||||
project_path: val.project_path,
|
||||
watch: val.watch,
|
||||
next_config: val.next_config,
|
||||
memory_limit: val.memory_limit.map(|m| m as _),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi(ts_return_type = "{ __napiType: \"Project\" }")]
|
||||
pub async fn project_new(options: NapiProjectOptions) -> napi::Result<External<VcArc<ProjectVc>>> {
|
||||
register();
|
||||
let turbo_tasks = TurboTasks::new(MemoryBackend::new(
|
||||
options
|
||||
.memory_limit
|
||||
.map(|m| m as usize)
|
||||
.unwrap_or(usize::MAX),
|
||||
));
|
||||
let options = options.into();
|
||||
let project = turbo_tasks
|
||||
.run_once(async move { ProjectVc::new(options).resolve().await })
|
||||
.await
|
||||
.map_err(|e| napi::Error::from_reason(PrettyPrintError(&e).to_string()))?;
|
||||
Ok(External::new_with_size_hint(
|
||||
VcArc::new(turbo_tasks, project),
|
||||
100,
|
||||
))
|
||||
}
|
||||
|
||||
#[napi(object)]
|
||||
#[derive(Default)]
|
||||
struct NapiRoute {
|
||||
/// The relative path from project_path to the route file
|
||||
pub pathname: String,
|
||||
|
||||
/// The type of route, eg a Page or App
|
||||
pub r#type: &'static str,
|
||||
|
||||
// Different representations of the endpoint
|
||||
pub endpoint: Option<External<VcArc<EndpointVc>>>,
|
||||
pub html_endpoint: Option<External<VcArc<EndpointVc>>>,
|
||||
pub rsc_endpoint: Option<External<VcArc<EndpointVc>>>,
|
||||
pub data_endpoint: Option<External<VcArc<EndpointVc>>>,
|
||||
}
|
||||
|
||||
impl NapiRoute {
|
||||
fn from_route(
|
||||
pathname: String,
|
||||
value: Route,
|
||||
turbo_tasks: &Arc<TurboTasks<MemoryBackend>>,
|
||||
) -> Self {
|
||||
let convert_endpoint =
|
||||
|endpoint: EndpointVc| Some(External::new(VcArc::new(turbo_tasks.clone(), endpoint)));
|
||||
match value {
|
||||
Route::Page {
|
||||
html_endpoint,
|
||||
data_endpoint,
|
||||
} => NapiRoute {
|
||||
pathname,
|
||||
r#type: "page",
|
||||
html_endpoint: convert_endpoint(html_endpoint),
|
||||
data_endpoint: convert_endpoint(data_endpoint),
|
||||
..Default::default()
|
||||
},
|
||||
Route::PageApi { endpoint } => NapiRoute {
|
||||
pathname,
|
||||
r#type: "page-api",
|
||||
endpoint: convert_endpoint(endpoint),
|
||||
..Default::default()
|
||||
},
|
||||
Route::AppPage {
|
||||
html_endpoint,
|
||||
rsc_endpoint,
|
||||
} => NapiRoute {
|
||||
pathname,
|
||||
r#type: "app-page",
|
||||
html_endpoint: convert_endpoint(html_endpoint),
|
||||
rsc_endpoint: convert_endpoint(rsc_endpoint),
|
||||
..Default::default()
|
||||
},
|
||||
Route::AppRoute { endpoint } => NapiRoute {
|
||||
pathname,
|
||||
r#type: "app-route",
|
||||
endpoint: convert_endpoint(endpoint),
|
||||
..Default::default()
|
||||
},
|
||||
Route::Conflict => NapiRoute {
|
||||
pathname,
|
||||
r#type: "conflict",
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[napi(object)]
|
||||
struct NapiMiddleware {
|
||||
pub endpoint: External<VcArc<EndpointVc>>,
|
||||
pub runtime: String,
|
||||
pub matcher: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl NapiMiddleware {
|
||||
fn from_middleware(
|
||||
value: &Middleware,
|
||||
turbo_tasks: &Arc<TurboTasks<MemoryBackend>>,
|
||||
) -> Result<Self> {
|
||||
Ok(NapiMiddleware {
|
||||
endpoint: External::new(VcArc::new(turbo_tasks.clone(), value.endpoint)),
|
||||
runtime: serde_enum_to_string(&value.config.runtime)?,
|
||||
matcher: value.config.matcher.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[napi(object)]
|
||||
struct NapiEntrypoints {
|
||||
pub routes: Vec<NapiRoute>,
|
||||
pub middleware: Option<NapiMiddleware>,
|
||||
pub issues: Vec<NapiIssue>,
|
||||
pub diagnostics: Vec<NapiDiagnostic>,
|
||||
}
|
||||
|
||||
#[napi(ts_return_type = "{ __napiType: \"RootTask\" }")]
|
||||
pub fn project_entrypoints_subscribe(
|
||||
#[napi(ts_arg_type = "{ __napiType: \"Project\" }")] project: External<VcArc<ProjectVc>>,
|
||||
func: JsFunction,
|
||||
) -> napi::Result<External<RootTask>> {
|
||||
let turbo_tasks = project.turbo_tasks().clone();
|
||||
let project = **project;
|
||||
subscribe(
|
||||
turbo_tasks.clone(),
|
||||
func,
|
||||
move || async move {
|
||||
let entrypoints = project.entrypoints();
|
||||
let entrypoints = entrypoints.strongly_consistent().await?;
|
||||
// TODO peek_issues and diagnostics
|
||||
Ok(entrypoints)
|
||||
},
|
||||
move |ctx| {
|
||||
let entrypoints = ctx.value;
|
||||
Ok(vec![NapiEntrypoints {
|
||||
routes: entrypoints
|
||||
.routes
|
||||
.iter()
|
||||
.map(|(pathname, &route)| {
|
||||
NapiRoute::from_route(pathname.clone(), route, &turbo_tasks)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
middleware: entrypoints
|
||||
.middleware
|
||||
.as_ref()
|
||||
.map(|m| NapiMiddleware::from_middleware(m, &turbo_tasks))
|
||||
.transpose()?,
|
||||
issues: vec![],
|
||||
diagnostics: vec![],
|
||||
}])
|
||||
},
|
||||
)
|
||||
}
|
137
packages/next-swc/crates/napi/src/next_api/utils.rs
Normal file
137
packages/next-swc/crates/napi/src/next_api/utils.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
use std::{future::Future, ops::Deref, sync::Arc};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use napi::{
|
||||
bindgen_prelude::{External, ToNapiValue},
|
||||
threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode},
|
||||
JsFunction, JsObject, NapiRaw, NapiValue, Status,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use turbo_tasks::{NothingVc, TaskId, TurboTasks};
|
||||
use turbopack_binding::{
|
||||
turbo::tasks_memory::MemoryBackend, turbopack::core::error::PrettyPrintError,
|
||||
};
|
||||
|
||||
/// A helper type to hold both a Vc operation and the TurboTasks root process.
|
||||
/// Without this, we'd need to pass both individually all over the place
|
||||
#[derive(Clone)]
|
||||
pub struct VcArc<T> {
|
||||
turbo_tasks: Arc<TurboTasks<MemoryBackend>>,
|
||||
/// The Vc. Must be resolved, otherwise you are referencing an inactive
|
||||
/// operation.
|
||||
vc: T,
|
||||
}
|
||||
|
||||
impl<T> VcArc<T> {
|
||||
pub fn new(turbo_tasks: Arc<TurboTasks<MemoryBackend>>, vc: T) -> Self {
|
||||
Self { turbo_tasks, vc }
|
||||
}
|
||||
|
||||
pub fn turbo_tasks(&self) -> &Arc<TurboTasks<MemoryBackend>> {
|
||||
&self.turbo_tasks
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for VcArc<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.vc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serde_enum_to_string<T: Serialize>(value: &T) -> Result<String> {
|
||||
Ok(serde_json::to_value(value)?
|
||||
.as_str()
|
||||
.context("value must serialize to a string")?
|
||||
.to_string())
|
||||
}
|
||||
|
||||
/// The root of our turbopack computation.
|
||||
pub struct RootTask {
|
||||
#[allow(dead_code)]
|
||||
turbo_tasks: Arc<TurboTasks<MemoryBackend>>,
|
||||
#[allow(dead_code)]
|
||||
task_id: Option<TaskId>,
|
||||
}
|
||||
|
||||
impl Drop for RootTask {
|
||||
fn drop(&mut self) {
|
||||
// TODO stop the root task
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn root_task_dispose(
|
||||
#[napi(ts_arg_type = "{ __napiType: \"RootTask\" }")] _root_task: External<RootTask>,
|
||||
) -> napi::Result<()> {
|
||||
// TODO(alexkirsz) Implement. Not panicking here to avoid crashing the process
|
||||
// when testing.
|
||||
eprintln!("root_task_dispose not yet implemented");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[napi(object)]
|
||||
pub struct NapiIssue {}
|
||||
|
||||
#[napi(object)]
|
||||
pub struct NapiDiagnostic {}
|
||||
|
||||
pub struct TurbopackResult<T: ToNapiValue> {
|
||||
pub result: T,
|
||||
pub issues: Vec<NapiIssue>,
|
||||
pub diagnostics: Vec<NapiDiagnostic>,
|
||||
}
|
||||
|
||||
impl<T: ToNapiValue> ToNapiValue for TurbopackResult<T> {
|
||||
unsafe fn to_napi_value(
|
||||
env: napi::sys::napi_env,
|
||||
val: Self,
|
||||
) -> napi::Result<napi::sys::napi_value> {
|
||||
let result = T::to_napi_value(env, val.result)?;
|
||||
// let issues = ToNapiValue::to_napi_value(env, val.issues)?;
|
||||
// let diagnostics = ToNapiValue::to_napi_value(env, val.diagnostics)?;
|
||||
|
||||
let result = JsObject::from_raw(env, result)?;
|
||||
|
||||
let mut obj = napi::Env::from_raw(env).create_object()?;
|
||||
for key in JsObject::keys(&result)? {
|
||||
obj.set_named_property(&key, result.get_named_property(&key)?)?;
|
||||
}
|
||||
obj.set_named_property("issues", val.issues)?;
|
||||
obj.set_named_property("diagnostics", val.diagnostics)?;
|
||||
|
||||
Ok(obj.raw())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe<T: 'static + Send + Sync, F: Future<Output = Result<T>> + Send, V: ToNapiValue>(
|
||||
turbo_tasks: Arc<TurboTasks<MemoryBackend>>,
|
||||
func: JsFunction,
|
||||
handler: impl 'static + Sync + Send + Clone + Fn() -> F,
|
||||
mapper: impl 'static + Sync + Send + FnMut(ThreadSafeCallContext<T>) -> napi::Result<Vec<V>>,
|
||||
) -> napi::Result<External<RootTask>> {
|
||||
let func: ThreadsafeFunction<T> = func.create_threadsafe_function(0, mapper)?;
|
||||
let task_id = turbo_tasks.spawn_root_task(move || {
|
||||
let handler = handler.clone();
|
||||
let func = func.clone();
|
||||
Box::pin(async move {
|
||||
let result = handler().await;
|
||||
|
||||
let status = func.call(
|
||||
result.map_err(|e| napi::Error::from_reason(PrettyPrintError(&e).to_string())),
|
||||
ThreadsafeFunctionCallMode::NonBlocking,
|
||||
);
|
||||
if !matches!(status, Status::Ok) {
|
||||
let error = anyhow!("Error calling JS function: {}", status);
|
||||
eprintln!("{}", error);
|
||||
return Err(error);
|
||||
}
|
||||
Ok(NothingVc::new().into())
|
||||
})
|
||||
});
|
||||
Ok(External::new(RootTask {
|
||||
turbo_tasks,
|
||||
task_id: Some(task_id),
|
||||
}))
|
||||
}
|
46
packages/next-swc/crates/next-api/Cargo.toml
Normal file
46
packages/next-swc/crates/next-api/Cargo.toml
Normal file
|
@ -0,0 +1,46 @@
|
|||
[package]
|
||||
name = "next-api"
|
||||
version = "0.1.0"
|
||||
description = "TBD"
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
autobenches = false
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[features]
|
||||
default = ["custom_allocator", "native-tls"]
|
||||
custom_allocator = ["turbopack-binding/__turbo_tasks_malloc", "turbopack-binding/__turbo_tasks_malloc_custom_allocator"]
|
||||
native-tls = ["next-core/native-tls"]
|
||||
rustls-tls = ["next-core/rustls-tls"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true, features = ["backtrace"] }
|
||||
futures = { workspace = true }
|
||||
indexmap = { workspace = true }
|
||||
next-core = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
turbopack-binding = { workspace = true, features = [
|
||||
"__turbo_tasks_memory",
|
||||
"__turbo_tasks_env",
|
||||
"__turbo_tasks_fs",
|
||||
"__turbopack",
|
||||
"__turbopack_build",
|
||||
"__turbopack_core",
|
||||
"__turbopack_dev",
|
||||
"__turbopack_env",
|
||||
"__turbopack_cli_utils",
|
||||
"__turbopack_node",
|
||||
"__turbopack_dev_server",
|
||||
]}
|
||||
turbo-tasks = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-subscriber = { workspace = true, features = ["env-filter", "json"] }
|
||||
|
||||
[build-dependencies]
|
||||
turbopack-binding = { workspace = true, features = [
|
||||
"__turbo_tasks_build"
|
||||
]}
|
5
packages/next-swc/crates/next-api/build.rs
Normal file
5
packages/next-swc/crates/next-api/build.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
use turbopack_binding::turbo::tasks_build::generate_register;
|
||||
|
||||
fn main() {
|
||||
generate_register();
|
||||
}
|
67
packages/next-swc/crates/next-api/src/app.rs
Normal file
67
packages/next-swc/crates/next-api/src/app.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use next_core::app_structure::Entrypoint;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use turbo_tasks::{trace::TraceRawVcs, CompletionVc};
|
||||
|
||||
use crate::route::{Endpoint, EndpointVc, Route, RouteVc, WrittenEndpointVc};
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub async fn app_entry_point_to_route(entrypoint: Entrypoint) -> RouteVc {
|
||||
match entrypoint {
|
||||
Entrypoint::AppPage { .. } => Route::AppPage {
|
||||
html_endpoint: AppPageEndpoint {
|
||||
ty: AppPageEndpointType::Html,
|
||||
}
|
||||
.cell()
|
||||
.into(),
|
||||
rsc_endpoint: AppPageEndpoint {
|
||||
ty: AppPageEndpointType::Rsc,
|
||||
}
|
||||
.cell()
|
||||
.into(),
|
||||
},
|
||||
Entrypoint::AppRoute { .. } => Route::AppRoute {
|
||||
endpoint: AppRouteEndpoint.cell().into(),
|
||||
},
|
||||
}
|
||||
.cell()
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Debug, TraceRawVcs)]
|
||||
enum AppPageEndpointType {
|
||||
Html,
|
||||
Rsc,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value]
|
||||
struct AppPageEndpoint {
|
||||
ty: AppPageEndpointType,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl Endpoint for AppPageEndpoint {
|
||||
#[turbo_tasks::function]
|
||||
fn write_to_disk(&self) -> WrittenEndpointVc {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn changed(&self) -> CompletionVc {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[turbo_tasks::value]
|
||||
struct AppRouteEndpoint;
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl Endpoint for AppRouteEndpoint {
|
||||
#[turbo_tasks::function]
|
||||
fn write_to_disk(&self) -> WrittenEndpointVc {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn changed(&self) -> CompletionVc {
|
||||
todo!()
|
||||
}
|
||||
}
|
13
packages/next-swc/crates/next-api/src/lib.rs
Normal file
13
packages/next-swc/crates/next-api/src/lib.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
#![feature(future_join)]
|
||||
#![feature(min_specialization)]
|
||||
|
||||
mod app;
|
||||
mod pages;
|
||||
pub mod project;
|
||||
pub mod route;
|
||||
|
||||
pub fn register() {
|
||||
next_core::register();
|
||||
turbopack_binding::turbopack::build::register();
|
||||
include!(concat!(env!("OUT_DIR"), "/register.rs"));
|
||||
}
|
363
packages/next-swc/crates/next-api/src/pages.rs
Normal file
363
packages/next-swc/crates/next-api/src/pages.rs
Normal file
|
@ -0,0 +1,363 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use indexmap::IndexMap;
|
||||
use next_core::{
|
||||
create_page_loader_entry_module, emit_all_assets, get_asset_path_from_pathname,
|
||||
pages_structure::{
|
||||
PagesDirectoryStructure, PagesDirectoryStructureVc, PagesStructure, PagesStructureItem,
|
||||
PagesStructureVc,
|
||||
},
|
||||
};
|
||||
use turbo_tasks::{primitives::StringVc, CompletionVc, CompletionsVc, Value};
|
||||
use turbopack_binding::{
|
||||
turbo::tasks_fs::FileSystemPathVc,
|
||||
turbopack::{
|
||||
core::{
|
||||
asset::Asset,
|
||||
changed::{any_content_changed, any_content_changed_of_output_assets},
|
||||
chunk::{ChunkableModule, ChunkingContext},
|
||||
context::AssetContext,
|
||||
file_source::FileSourceVc,
|
||||
output::{OutputAssetVc, OutputAssetsVc},
|
||||
reference_type::{EntryReferenceSubType, ReferenceType},
|
||||
source::SourceVc,
|
||||
},
|
||||
ecmascript::EcmascriptModuleAssetVc,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
project::ProjectVc,
|
||||
route::{Endpoint, EndpointVc, Route, RoutesVc, WrittenEndpoint, WrittenEndpointVc},
|
||||
};
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub async fn get_pages_routes(
|
||||
project: ProjectVc,
|
||||
page_structure: PagesStructureVc,
|
||||
) -> Result<RoutesVc> {
|
||||
let PagesStructure { api, pages, .. } = *page_structure.await?;
|
||||
let mut routes = IndexMap::new();
|
||||
async fn add_dir_to_routes(
|
||||
routes: &mut IndexMap<String, Route>,
|
||||
dir: PagesDirectoryStructureVc,
|
||||
make_route: impl Fn(StringVc, StringVc, FileSystemPathVc) -> Route,
|
||||
) -> Result<()> {
|
||||
let mut queue = vec![dir];
|
||||
while let Some(dir) = queue.pop() {
|
||||
let PagesDirectoryStructure {
|
||||
ref items,
|
||||
ref children,
|
||||
next_router_path: _,
|
||||
project_path: _,
|
||||
} = *dir.await?;
|
||||
for &item in items.iter() {
|
||||
let PagesStructureItem {
|
||||
next_router_path,
|
||||
project_path,
|
||||
original_path,
|
||||
} = *item.await?;
|
||||
let pathname = format!("/{}", next_router_path.await?.path);
|
||||
let pathname_vc = StringVc::cell(pathname.clone());
|
||||
let original_name = StringVc::cell(format!("/{}", original_path.await?.path));
|
||||
let route = make_route(pathname_vc, original_name, project_path);
|
||||
routes.insert(pathname, route);
|
||||
}
|
||||
for &child in children.iter() {
|
||||
queue.push(child);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
if let Some(api) = api {
|
||||
add_dir_to_routes(&mut routes, api, |pathname, original_name, path| {
|
||||
Route::PageApi {
|
||||
endpoint: ApiEndpointVc::new(project, pathname, original_name, path).into(),
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
if let Some(page) = pages {
|
||||
add_dir_to_routes(&mut routes, page, |pathname, original_name, path| {
|
||||
Route::Page {
|
||||
html_endpoint: PageHtmlEndpointVc::new(project, pathname, original_name, path)
|
||||
.into(),
|
||||
data_endpoint: PageDataEndpointVc::new(project, pathname, original_name, path)
|
||||
.into(),
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Ok(RoutesVc::cell(routes))
|
||||
}
|
||||
|
||||
#[turbo_tasks::value]
|
||||
struct PageHtmlEndpoint {
|
||||
project: ProjectVc,
|
||||
pathname: StringVc,
|
||||
original_name: StringVc,
|
||||
path: FileSystemPathVc,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl PageHtmlEndpointVc {
|
||||
#[turbo_tasks::function]
|
||||
fn new(
|
||||
project: ProjectVc,
|
||||
pathname: StringVc,
|
||||
original_name: StringVc,
|
||||
path: FileSystemPathVc,
|
||||
) -> Self {
|
||||
PageHtmlEndpoint {
|
||||
project,
|
||||
pathname,
|
||||
original_name,
|
||||
path,
|
||||
}
|
||||
.cell()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn source(self) -> Result<SourceVc> {
|
||||
let this = self.await?;
|
||||
Ok(FileSourceVc::new(this.path).into())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn client_chunks(self) -> Result<OutputAssetsVc> {
|
||||
let this = self.await?;
|
||||
|
||||
let client_module = create_page_loader_entry_module(
|
||||
this.project.pages_client_module_context(),
|
||||
self.source(),
|
||||
this.pathname,
|
||||
);
|
||||
|
||||
let Some(client_module) = EcmascriptModuleAssetVc::resolve_from(client_module).await?
|
||||
else {
|
||||
bail!("expected an ECMAScript module asset");
|
||||
};
|
||||
|
||||
let client_chunking_context = this.project.client_chunking_context();
|
||||
|
||||
let client_entry_chunk = client_module.as_root_chunk(client_chunking_context.into());
|
||||
|
||||
let client_chunks = client_chunking_context.evaluated_chunk_group(
|
||||
client_entry_chunk,
|
||||
this.project
|
||||
.pages_client_runtime_entries()
|
||||
.with_entry(client_module.into()),
|
||||
);
|
||||
|
||||
Ok(client_chunks)
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn ssr_chunk(self) -> Result<OutputAssetVc> {
|
||||
let this = self.await?;
|
||||
let reference_type = Value::new(ReferenceType::Entry(EntryReferenceSubType::Page));
|
||||
|
||||
let ssr_module = this
|
||||
.project
|
||||
.pages_ssr_module_context()
|
||||
.process(self.source(), reference_type.clone());
|
||||
|
||||
let Some(ssr_module) = EcmascriptModuleAssetVc::resolve_from(ssr_module).await? else {
|
||||
bail!("expected an ECMAScript module asset");
|
||||
};
|
||||
|
||||
let asset_path = get_asset_path_from_pathname(&this.pathname.await?, ".js");
|
||||
|
||||
let ssr_entry_chunk_path_string = format!("server/pages{asset_path}");
|
||||
let ssr_entry_chunk_path = this.project.node_root().join(&ssr_entry_chunk_path_string);
|
||||
let ssr_entry_chunk = this.project.ssr_chunking_context().entry_chunk(
|
||||
ssr_entry_chunk_path,
|
||||
ssr_module.into(),
|
||||
this.project.pages_ssr_runtime_entries(),
|
||||
);
|
||||
|
||||
Ok(ssr_entry_chunk)
|
||||
}
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl Endpoint for PageHtmlEndpoint {
|
||||
#[turbo_tasks::function]
|
||||
async fn write_to_disk(self_vc: PageHtmlEndpointVc) -> Result<WrittenEndpointVc> {
|
||||
let this = self_vc.await?;
|
||||
let ssr_chunk = self_vc.ssr_chunk();
|
||||
let ssr_emit = emit_all_assets(
|
||||
OutputAssetsVc::cell(vec![ssr_chunk]),
|
||||
this.project.node_root(),
|
||||
this.project.client_root().join("_next"),
|
||||
this.project.node_root(),
|
||||
);
|
||||
let client_emit = emit_all_assets(
|
||||
self_vc.client_chunks(),
|
||||
this.project.node_root(),
|
||||
this.project.client_root().join("_next"),
|
||||
this.project.node_root(),
|
||||
);
|
||||
|
||||
ssr_emit.await?;
|
||||
client_emit.await?;
|
||||
|
||||
Ok(WrittenEndpoint {
|
||||
server_entry_path: this
|
||||
.project
|
||||
.node_root()
|
||||
.await?
|
||||
.get_path_to(&*ssr_chunk.ident().path().await?)
|
||||
.context("ssr chunk entry path must be inside the node root")?
|
||||
.to_string(),
|
||||
server_paths: vec![],
|
||||
}
|
||||
.cell())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn changed(self_vc: PageHtmlEndpointVc) -> CompletionVc {
|
||||
let ssr_chunk = self_vc.ssr_chunk();
|
||||
CompletionsVc::all(vec![
|
||||
any_content_changed(ssr_chunk.into()),
|
||||
any_content_changed_of_output_assets(self_vc.client_chunks()),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[turbo_tasks::value]
|
||||
struct PageDataEndpoint {
|
||||
project: ProjectVc,
|
||||
pathname: StringVc,
|
||||
original_name: StringVc,
|
||||
path: FileSystemPathVc,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl PageDataEndpointVc {
|
||||
#[turbo_tasks::function]
|
||||
fn new(
|
||||
project: ProjectVc,
|
||||
pathname: StringVc,
|
||||
original_name: StringVc,
|
||||
path: FileSystemPathVc,
|
||||
) -> Self {
|
||||
PageDataEndpoint {
|
||||
project,
|
||||
pathname,
|
||||
original_name,
|
||||
path,
|
||||
}
|
||||
.cell()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn source(self) -> Result<SourceVc> {
|
||||
let this = self.await?;
|
||||
Ok(FileSourceVc::new(this.path).into())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn ssr_data_chunk(self) -> Result<OutputAssetVc> {
|
||||
let this = self.await?;
|
||||
let reference_type = Value::new(ReferenceType::Entry(EntryReferenceSubType::Page));
|
||||
|
||||
let ssr_data_module = this
|
||||
.project
|
||||
.pages_ssr_data_module_context()
|
||||
.process(self.source(), reference_type.clone());
|
||||
|
||||
let Some(ssr_data_module) = EcmascriptModuleAssetVc::resolve_from(ssr_data_module).await?
|
||||
else {
|
||||
bail!("expected an ECMAScript module asset");
|
||||
};
|
||||
|
||||
let asset_path = get_asset_path_from_pathname(&this.pathname.await?, ".js");
|
||||
|
||||
let ssr_data_entry_chunk_path_string = format!("server/pages-data/{asset_path}");
|
||||
let ssr_data_entry_chunk_path = this
|
||||
.project
|
||||
.node_root()
|
||||
.join(&ssr_data_entry_chunk_path_string);
|
||||
let ssr_data_entry_chunk = this.project.ssr_data_chunking_context().entry_chunk(
|
||||
ssr_data_entry_chunk_path,
|
||||
ssr_data_module.into(),
|
||||
this.project.pages_ssr_runtime_entries(),
|
||||
);
|
||||
|
||||
Ok(ssr_data_entry_chunk)
|
||||
}
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl Endpoint for PageDataEndpoint {
|
||||
#[turbo_tasks::function]
|
||||
async fn write_to_disk(self_vc: PageDataEndpointVc) -> Result<WrittenEndpointVc> {
|
||||
let this = self_vc.await?;
|
||||
let ssr_data_chunk = self_vc.ssr_data_chunk();
|
||||
emit_all_assets(
|
||||
OutputAssetsVc::cell(vec![ssr_data_chunk]),
|
||||
this.project.node_root(),
|
||||
this.project.client_root().join("_next"),
|
||||
this.project.node_root(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(WrittenEndpoint {
|
||||
server_entry_path: this
|
||||
.project
|
||||
.node_root()
|
||||
.await?
|
||||
.get_path_to(&*ssr_data_chunk.ident().path().await?)
|
||||
.context("ssr data chunk entry path must be inside the node root")?
|
||||
.to_string(),
|
||||
server_paths: vec![],
|
||||
}
|
||||
.cell())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn changed(self_vc: PageDataEndpointVc) -> Result<CompletionVc> {
|
||||
let ssr_data_chunk = self_vc.ssr_data_chunk();
|
||||
Ok(any_content_changed(ssr_data_chunk.into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[turbo_tasks::value]
|
||||
struct ApiEndpoint {
|
||||
project: ProjectVc,
|
||||
pathname: StringVc,
|
||||
original_name: StringVc,
|
||||
path: FileSystemPathVc,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl ApiEndpointVc {
|
||||
#[turbo_tasks::function]
|
||||
fn new(
|
||||
project: ProjectVc,
|
||||
pathname: StringVc,
|
||||
original_name: StringVc,
|
||||
path: FileSystemPathVc,
|
||||
) -> Self {
|
||||
ApiEndpoint {
|
||||
project,
|
||||
pathname,
|
||||
original_name,
|
||||
path,
|
||||
}
|
||||
.cell()
|
||||
}
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl Endpoint for ApiEndpoint {
|
||||
#[turbo_tasks::function]
|
||||
fn write_to_disk(&self) -> WrittenEndpointVc {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn changed(&self) -> CompletionVc {
|
||||
todo!()
|
||||
}
|
||||
}
|
517
packages/next-swc/crates/next-api/src/project.rs
Normal file
517
packages/next-swc/crates/next-api/src/project.rs
Normal file
|
@ -0,0 +1,517 @@
|
|||
use std::path::MAIN_SEPARATOR;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use indexmap::{map::Entry, IndexMap};
|
||||
use next_core::{
|
||||
app_structure::{find_app_dir, get_entrypoints},
|
||||
mode::NextMode,
|
||||
next_client::{
|
||||
get_client_chunking_context, get_client_compile_time_info,
|
||||
get_client_module_options_context, get_client_resolve_options_context,
|
||||
get_client_runtime_entries, ClientContextType,
|
||||
},
|
||||
next_config::NextConfigVc,
|
||||
next_dynamic::NextDynamicTransitionVc,
|
||||
next_server::{
|
||||
get_server_chunking_context, get_server_compile_time_info,
|
||||
get_server_module_options_context, get_server_resolve_options_context,
|
||||
get_server_runtime_entries, ServerContextType,
|
||||
},
|
||||
pages_structure::{find_pages_structure, PagesStructureVc},
|
||||
util::NextSourceConfig,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use turbo_tasks::{
|
||||
debug::ValueDebugFormat, trace::TraceRawVcs, NothingVc, TaskInput, TransientValue,
|
||||
TryJoinIterExt, Value,
|
||||
};
|
||||
use turbopack_binding::{
|
||||
turbo::{
|
||||
tasks_env::ProcessEnvVc,
|
||||
tasks_fs::{
|
||||
DiskFileSystemVc, FileSystem, FileSystemPathVc, FileSystemVc, VirtualFileSystemVc,
|
||||
},
|
||||
},
|
||||
turbopack::{
|
||||
build::BuildChunkingContextVc,
|
||||
core::{
|
||||
chunk::{ChunkingContext, EvaluatableAssetsVc},
|
||||
compile_time_info::CompileTimeInfoVc,
|
||||
context::AssetContextVc,
|
||||
environment::ServerAddrVc,
|
||||
PROJECT_FILESYSTEM_NAME,
|
||||
},
|
||||
dev::DevChunkingContextVc,
|
||||
ecmascript::chunk::EcmascriptChunkingContextVc,
|
||||
env::dotenv::load_env,
|
||||
node::execution_context::ExecutionContextVc,
|
||||
turbopack::{
|
||||
evaluate_context::node_build_environment,
|
||||
module_options::ModuleOptionsContextVc,
|
||||
resolve_options_context::ResolveOptionsContextVc,
|
||||
transition::{ContextTransitionVc, TransitionsByNameVc},
|
||||
ModuleAssetContextVc,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
app::app_entry_point_to_route,
|
||||
pages::get_pages_routes,
|
||||
route::{EndpointVc, Route},
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, TaskInput)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProjectOptions {
|
||||
/// A root path from which all files must be nested under. Trying to access
|
||||
/// a file outside this root will fail. Think of this as a chroot.
|
||||
pub root_path: String,
|
||||
|
||||
/// A path inside the root_path which contains the app/pages directories.
|
||||
pub project_path: String,
|
||||
|
||||
/// The contents of next.config.js, serialized to JSON.
|
||||
pub next_config: String,
|
||||
|
||||
/// Whether to watch the filesystem for file changes.
|
||||
pub watch: bool,
|
||||
|
||||
/// An upper bound of memory that turbopack will attempt to stay under.
|
||||
pub memory_limit: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, TraceRawVcs, PartialEq, Eq, ValueDebugFormat)]
|
||||
pub struct Middleware {
|
||||
pub endpoint: EndpointVc,
|
||||
pub config: NextSourceConfig,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value]
|
||||
pub struct Entrypoints {
|
||||
pub routes: IndexMap<String, Route>,
|
||||
pub middleware: Option<Middleware>,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value]
|
||||
pub struct Project {
|
||||
/// A root path from which all files must be nested under. Trying to access
|
||||
/// a file outside this root will fail. Think of this as a chroot.
|
||||
root_path: String,
|
||||
|
||||
/// A path inside the root_path which contains the app/pages directories.
|
||||
project_path: String,
|
||||
|
||||
/// Whether to watch the filesystem for file changes.
|
||||
watch: bool,
|
||||
|
||||
/// Next config.
|
||||
next_config: NextConfigVc,
|
||||
|
||||
browserslist_query: String,
|
||||
|
||||
mode: NextMode,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl ProjectVc {
|
||||
#[turbo_tasks::function]
|
||||
pub async fn new(options: ProjectOptions) -> Result<Self> {
|
||||
let next_config = NextConfigVc::from_string(options.next_config);
|
||||
Ok(Project {
|
||||
root_path: options.root_path,
|
||||
project_path: options.project_path,
|
||||
watch: options.watch,
|
||||
next_config,
|
||||
browserslist_query: "last 1 Chrome versions, last 1 Firefox versions, last 1 Safari \
|
||||
versions, last 1 Edge versions"
|
||||
.to_string(),
|
||||
mode: NextMode::Development,
|
||||
}
|
||||
.cell())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn project_fs(self) -> Result<FileSystemVc> {
|
||||
let this = self.await?;
|
||||
let disk_fs = DiskFileSystemVc::new(
|
||||
PROJECT_FILESYSTEM_NAME.to_string(),
|
||||
this.root_path.to_string(),
|
||||
);
|
||||
if this.watch {
|
||||
disk_fs.await?.start_watching_with_invalidation_reason()?;
|
||||
}
|
||||
Ok(disk_fs.into())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn client_fs(self) -> Result<FileSystemVc> {
|
||||
let virtual_fs = VirtualFileSystemVc::new();
|
||||
Ok(virtual_fs.into())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn node_fs(self) -> Result<FileSystemVc> {
|
||||
let this = self.await?;
|
||||
let disk_fs = DiskFileSystemVc::new("node".to_string(), this.project_path.clone());
|
||||
disk_fs.await?.start_watching_with_invalidation_reason()?;
|
||||
Ok(disk_fs.into())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) fn node_root(self) -> FileSystemPathVc {
|
||||
self.node_fs().root().join(".next")
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) fn client_root(self) -> FileSystemPathVc {
|
||||
self.client_fs().root()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn project_root_path(self) -> FileSystemPathVc {
|
||||
self.project_fs().root()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn project_path(self) -> Result<FileSystemPathVc> {
|
||||
let this = self.await?;
|
||||
let root = self.project_root_path();
|
||||
let project_relative = this.project_path.strip_prefix(&this.root_path).unwrap();
|
||||
let project_relative = project_relative
|
||||
.strip_prefix(MAIN_SEPARATOR)
|
||||
.unwrap_or(project_relative)
|
||||
.replace(MAIN_SEPARATOR, "/");
|
||||
Ok(root.join(&project_relative))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn pages_structure(self) -> Result<PagesStructureVc> {
|
||||
let this: turbo_tasks::ReadRef<Project> = self.await?;
|
||||
let next_router_fs = VirtualFileSystemVc::new().as_file_system();
|
||||
let next_router_root = next_router_fs.root();
|
||||
Ok(find_pages_structure(
|
||||
self.project_path(),
|
||||
next_router_root,
|
||||
this.next_config.page_extensions(),
|
||||
))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn env(self) -> ProcessEnvVc {
|
||||
load_env(self.project_path())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn next_config(self) -> Result<NextConfigVc> {
|
||||
Ok(self.await?.next_config)
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn execution_context(self) -> ExecutionContextVc {
|
||||
let node_root = self.node_root();
|
||||
|
||||
let node_execution_chunking_context = DevChunkingContextVc::builder(
|
||||
self.project_path(),
|
||||
node_root,
|
||||
node_root.join("chunks"),
|
||||
node_root.join("assets"),
|
||||
node_build_environment(),
|
||||
)
|
||||
.build()
|
||||
.into();
|
||||
|
||||
ExecutionContextVc::new(
|
||||
self.project_path(),
|
||||
node_execution_chunking_context,
|
||||
self.env(),
|
||||
)
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn client_compile_time_info(self) -> Result<CompileTimeInfoVc> {
|
||||
let this = self.await?;
|
||||
Ok(get_client_compile_time_info(
|
||||
this.mode,
|
||||
&this.browserslist_query,
|
||||
))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn server_compile_time_info(self) -> Result<CompileTimeInfoVc> {
|
||||
let this = self.await?;
|
||||
Ok(get_server_compile_time_info(
|
||||
this.mode,
|
||||
self.env(),
|
||||
// TODO(alexkirsz) Fill this out.
|
||||
ServerAddrVc::empty(),
|
||||
))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn pages_dir(self) -> Result<FileSystemPathVc> {
|
||||
Ok(if let Some(pages) = self.pages_structure().await?.pages {
|
||||
pages.project_path()
|
||||
} else {
|
||||
self.project_path().join("pages")
|
||||
})
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn pages_transitions(self) -> TransitionsByNameVc {
|
||||
TransitionsByNameVc::cell(
|
||||
[(
|
||||
"next-dynamic".to_string(),
|
||||
NextDynamicTransitionVc::new(self.pages_client_transition()).into(),
|
||||
)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn pages_client_transition(self) -> ContextTransitionVc {
|
||||
ContextTransitionVc::new(
|
||||
self.client_compile_time_info(),
|
||||
self.pages_client_module_options_context(),
|
||||
self.pages_client_resolve_options_context(),
|
||||
)
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn pages_client_module_options_context(self) -> Result<ModuleOptionsContextVc> {
|
||||
let this = self.await?;
|
||||
Ok(get_client_module_options_context(
|
||||
self.project_path(),
|
||||
self.execution_context(),
|
||||
self.client_compile_time_info().environment(),
|
||||
Value::new(ClientContextType::Pages {
|
||||
pages_dir: self.pages_dir(),
|
||||
}),
|
||||
this.mode,
|
||||
self.next_config(),
|
||||
))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn pages_client_resolve_options_context(self) -> Result<ResolveOptionsContextVc> {
|
||||
let this = self.await?;
|
||||
Ok(get_client_resolve_options_context(
|
||||
self.project_path(),
|
||||
Value::new(ClientContextType::Pages {
|
||||
pages_dir: self.pages_dir(),
|
||||
}),
|
||||
this.mode,
|
||||
self.next_config(),
|
||||
self.execution_context(),
|
||||
))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) fn pages_client_module_context(self) -> AssetContextVc {
|
||||
ModuleAssetContextVc::new(
|
||||
self.pages_transitions(),
|
||||
self.client_compile_time_info(),
|
||||
self.pages_client_module_options_context(),
|
||||
self.pages_client_resolve_options_context(),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) fn pages_ssr_module_context(self) -> AssetContextVc {
|
||||
ModuleAssetContextVc::new(
|
||||
self.pages_transitions(),
|
||||
self.server_compile_time_info(),
|
||||
self.pages_ssr_module_options_context(),
|
||||
self.pages_ssr_resolve_options_context(),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) fn pages_ssr_data_module_context(self) -> AssetContextVc {
|
||||
ModuleAssetContextVc::new(
|
||||
self.pages_transitions(),
|
||||
self.server_compile_time_info(),
|
||||
self.pages_ssr_data_module_options_context(),
|
||||
self.pages_ssr_resolve_options_context(),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn pages_ssr_module_options_context(self) -> Result<ModuleOptionsContextVc> {
|
||||
let this = self.await?;
|
||||
Ok(get_server_module_options_context(
|
||||
self.project_path(),
|
||||
self.execution_context(),
|
||||
Value::new(ServerContextType::Pages {
|
||||
pages_dir: self.pages_dir(),
|
||||
}),
|
||||
this.mode,
|
||||
self.next_config(),
|
||||
))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn pages_ssr_data_module_options_context(self) -> Result<ModuleOptionsContextVc> {
|
||||
let this = self.await?;
|
||||
Ok(get_server_module_options_context(
|
||||
self.project_path(),
|
||||
self.execution_context(),
|
||||
Value::new(ServerContextType::PagesData {
|
||||
pages_dir: self.pages_dir(),
|
||||
}),
|
||||
this.mode,
|
||||
self.next_config(),
|
||||
))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn pages_ssr_resolve_options_context(self) -> Result<ResolveOptionsContextVc> {
|
||||
let this = self.await?;
|
||||
Ok(get_server_resolve_options_context(
|
||||
self.project_path(),
|
||||
Value::new(ServerContextType::Pages {
|
||||
pages_dir: self.pages_dir(),
|
||||
}),
|
||||
this.mode,
|
||||
self.next_config(),
|
||||
self.execution_context(),
|
||||
))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) async fn pages_client_runtime_entries(self) -> Result<EvaluatableAssetsVc> {
|
||||
let this = self.await?;
|
||||
let client_runtime_entries = get_client_runtime_entries(
|
||||
self.project_path(),
|
||||
self.env(),
|
||||
Value::new(ClientContextType::Pages {
|
||||
pages_dir: self.pages_dir(),
|
||||
}),
|
||||
this.mode,
|
||||
self.next_config(),
|
||||
self.execution_context(),
|
||||
);
|
||||
Ok(client_runtime_entries.resolve_entries(self.pages_client_module_context()))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) async fn pages_ssr_runtime_entries(self) -> Result<EvaluatableAssetsVc> {
|
||||
let this = self.await?;
|
||||
let ssr_runtime_entries = get_server_runtime_entries(
|
||||
self.project_path(),
|
||||
self.env(),
|
||||
Value::new(ServerContextType::Pages {
|
||||
pages_dir: self.pages_dir(),
|
||||
}),
|
||||
this.mode,
|
||||
self.next_config(),
|
||||
);
|
||||
Ok(ssr_runtime_entries.resolve_entries(self.pages_ssr_module_context()))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) async fn client_chunking_context(self) -> Result<EcmascriptChunkingContextVc> {
|
||||
let this = self.await?;
|
||||
Ok(get_client_chunking_context(
|
||||
self.project_path(),
|
||||
self.client_root(),
|
||||
self.client_compile_time_info().environment(),
|
||||
this.mode,
|
||||
))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) fn server_chunking_context(self) -> BuildChunkingContextVc {
|
||||
get_server_chunking_context(
|
||||
self.project_path(),
|
||||
self.node_root(),
|
||||
self.client_fs().root(),
|
||||
self.server_compile_time_info().environment(),
|
||||
)
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) async fn ssr_chunking_context(self) -> Result<BuildChunkingContextVc> {
|
||||
let ssr_chunking_context = self.server_chunking_context().with_layer("ssr");
|
||||
BuildChunkingContextVc::resolve_from(ssr_chunking_context)
|
||||
.await?
|
||||
.context("with_layer should not change the type of the chunking context")
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) async fn ssr_data_chunking_context(self) -> Result<BuildChunkingContextVc> {
|
||||
let ssr_chunking_context = self.server_chunking_context().with_layer("ssr data");
|
||||
BuildChunkingContextVc::resolve_from(ssr_chunking_context)
|
||||
.await?
|
||||
.context("with_layer should not change the type of the chunking context")
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub(super) async fn rsc_chunking_context(self) -> Result<BuildChunkingContextVc> {
|
||||
let rsc_chunking_context = self.server_chunking_context().with_layer("rsc");
|
||||
BuildChunkingContextVc::resolve_from(rsc_chunking_context)
|
||||
.await?
|
||||
.context("with_layer should not change the type of the chunking context")
|
||||
}
|
||||
|
||||
/// Scans the app/pages directories for entry points files (matching the
|
||||
/// provided page_extensions).
|
||||
#[turbo_tasks::function]
|
||||
pub async fn entrypoints(self) -> Result<EntrypointsVc> {
|
||||
let this = self.await?;
|
||||
let mut routes = IndexMap::new();
|
||||
if let Some(app_dir) = *find_app_dir(self.project_path()).await? {
|
||||
let app_entrypoints = get_entrypoints(app_dir, this.next_config.page_extensions());
|
||||
routes.extend(
|
||||
app_entrypoints
|
||||
.await?
|
||||
.iter()
|
||||
.map(|(pathname, app_entrypoint)| async {
|
||||
Ok((
|
||||
pathname.clone(),
|
||||
*app_entry_point_to_route(*app_entrypoint).await?,
|
||||
))
|
||||
})
|
||||
.try_join()
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
for (pathname, page_route) in get_pages_routes(self, self.pages_structure()).await?.iter() {
|
||||
match routes.entry(pathname.clone()) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
*entry.get_mut() = Route::Conflict;
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(*page_route);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO middleware
|
||||
Ok(Entrypoints {
|
||||
routes,
|
||||
middleware: None,
|
||||
}
|
||||
.cell())
|
||||
}
|
||||
|
||||
/// Emits opaque HMR events whenever a change is detected in the chunk group
|
||||
/// internally known as `identifier`.
|
||||
#[turbo_tasks::function]
|
||||
pub fn hmr_events(self, _identifier: String, _sender: TransientValue<()>) -> NothingVc {
|
||||
NothingVc::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn project_fs(project_dir: &str, watching: bool) -> Result<FileSystemVc> {
|
||||
let disk_fs =
|
||||
DiskFileSystemVc::new(PROJECT_FILESYSTEM_NAME.to_string(), project_dir.to_string());
|
||||
if watching {
|
||||
disk_fs.await?.start_watching_with_invalidation_reason()?;
|
||||
}
|
||||
Ok(disk_fs.into())
|
||||
}
|
43
packages/next-swc/crates/next-api/src/route.rs
Normal file
43
packages/next-swc/crates/next-api/src/route.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use anyhow::Result;
|
||||
use indexmap::IndexMap;
|
||||
use turbo_tasks::CompletionVc;
|
||||
|
||||
#[turbo_tasks::value(shared)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Route {
|
||||
Page {
|
||||
html_endpoint: EndpointVc,
|
||||
data_endpoint: EndpointVc,
|
||||
},
|
||||
PageApi {
|
||||
endpoint: EndpointVc,
|
||||
},
|
||||
AppPage {
|
||||
html_endpoint: EndpointVc,
|
||||
rsc_endpoint: EndpointVc,
|
||||
},
|
||||
AppRoute {
|
||||
endpoint: EndpointVc,
|
||||
},
|
||||
Conflict,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_trait]
|
||||
pub trait Endpoint {
|
||||
fn write_to_disk(&self) -> WrittenEndpointVc;
|
||||
fn changed(&self) -> CompletionVc;
|
||||
}
|
||||
|
||||
#[turbo_tasks::value(shared)]
|
||||
#[derive(Debug)]
|
||||
pub struct WrittenEndpoint {
|
||||
/// Relative to the root_path
|
||||
pub server_entry_path: String,
|
||||
/// Relative to the root_path
|
||||
pub server_paths: Vec<String>,
|
||||
}
|
||||
|
||||
/// The routes as map from pathname to route. (pathname includes the leading
|
||||
/// slash)
|
||||
#[turbo_tasks::value(transparent)]
|
||||
pub struct Routes(IndexMap<String, Route>);
|
|
@ -68,7 +68,11 @@ pub async fn get_page_entries(
|
|||
server_compile_time_info: CompileTimeInfoVc,
|
||||
next_config: NextConfigVc,
|
||||
) -> Result<PageEntriesVc> {
|
||||
let pages_structure = find_pages_structure(project_root, next_router_root, next_config);
|
||||
let pages_structure = find_pages_structure(
|
||||
project_root,
|
||||
next_router_root,
|
||||
next_config.page_extensions(),
|
||||
);
|
||||
|
||||
let pages_dir = if let Some(pages) = pages_structure.await?.pages {
|
||||
pages.project_path().resolve().await?
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
"check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/turbopack-ecmascript-runtime": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.2",
|
||||
"@vercel/turbopack-node": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230713.2",
|
||||
"@vercel/turbopack-ecmascript-runtime": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.3",
|
||||
"@vercel/turbopack-node": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230713.3",
|
||||
"anser": "^2.1.1",
|
||||
"css.escape": "^1.5.1",
|
||||
"next": "*",
|
||||
|
|
|
@ -16,10 +16,6 @@ const loadNextConfig = async (silent) => {
|
|||
|
||||
const customRoutes = await loadCustomRoutes(nextConfig)
|
||||
|
||||
nextConfig.headers = customRoutes.headers
|
||||
nextConfig.rewrites = customRoutes.rewrites
|
||||
nextConfig.redirects = customRoutes.redirects
|
||||
|
||||
// TODO: these functions takes arguments, have to be supported in a different way
|
||||
nextConfig.exportPathMap = nextConfig.exportPathMap && {}
|
||||
nextConfig.webpack = nextConfig.webpack && {}
|
||||
|
@ -30,7 +26,10 @@ const loadNextConfig = async (silent) => {
|
|||
)
|
||||
}
|
||||
|
||||
return nextConfig
|
||||
return {
|
||||
customRoutes: customRoutes,
|
||||
config: nextConfig,
|
||||
}
|
||||
}
|
||||
|
||||
export { loadNextConfig as default }
|
||||
|
|
|
@ -12,7 +12,7 @@ use turbo_tasks::{
|
|||
debug::ValueDebugFormat,
|
||||
primitives::{StringVc, StringsVc},
|
||||
trace::TraceRawVcs,
|
||||
CompletionVc, CompletionsVc,
|
||||
CompletionVc, CompletionsVc, TaskInput, ValueToString,
|
||||
};
|
||||
use turbopack_binding::{
|
||||
turbo::tasks_fs::{DirectoryContent, DirectoryEntry, FileSystemEntryType, FileSystemPathVc},
|
||||
|
@ -297,11 +297,11 @@ fn match_metadata_file<'a>(
|
|||
|
||||
#[turbo_tasks::function]
|
||||
async fn get_directory_tree(
|
||||
app_dir: FileSystemPathVc,
|
||||
dir: FileSystemPathVc,
|
||||
page_extensions: StringsVc,
|
||||
) -> Result<DirectoryTreeVc> {
|
||||
let DirectoryContent::Entries(entries) = &*app_dir.read_dir().await? else {
|
||||
bail!("app_dir must be a directory")
|
||||
let DirectoryContent::Entries(entries) = &*dir.read_dir().await? else {
|
||||
bail!("{} must be a directory", dir.to_string().await?);
|
||||
};
|
||||
let page_extensions_value = page_extensions.await?;
|
||||
|
||||
|
@ -451,7 +451,16 @@ async fn merge_loader_trees(
|
|||
}
|
||||
|
||||
#[derive(
|
||||
Copy, Clone, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
TraceRawVcs,
|
||||
ValueDebugFormat,
|
||||
Debug,
|
||||
TaskInput,
|
||||
)]
|
||||
pub enum Entrypoint {
|
||||
AppPage { loader_tree: LoaderTreeVc },
|
||||
|
|
97
packages/next-swc/crates/next-core/src/emit.rs
Normal file
97
packages/next-swc/crates/next-core/src/emit.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use anyhow::Result;
|
||||
use turbo_tasks::{
|
||||
graph::{AdjacencyMap, GraphTraversal},
|
||||
CompletionVc, CompletionsVc, TryJoinIterExt,
|
||||
};
|
||||
use turbo_tasks_fs::{rebase, FileSystemPathVc};
|
||||
use turbopack_binding::turbopack::core::{
|
||||
asset::Asset,
|
||||
output::{OutputAssetVc, OutputAssetsVc},
|
||||
reference::AssetReference,
|
||||
};
|
||||
|
||||
/// Emits all assets transitively reachable from the given chunks, that are
|
||||
/// inside the node root or the client root.
|
||||
#[turbo_tasks::function]
|
||||
pub async fn emit_all_assets(
|
||||
assets: OutputAssetsVc,
|
||||
node_root: FileSystemPathVc,
|
||||
client_relative_path: FileSystemPathVc,
|
||||
client_output_path: FileSystemPathVc,
|
||||
) -> Result<CompletionVc> {
|
||||
let all_assets = all_assets_from_entries(assets).await?;
|
||||
Ok(CompletionsVc::all(
|
||||
all_assets
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|asset| async move {
|
||||
if asset.ident().path().await?.is_inside(&*node_root.await?) {
|
||||
return Ok(emit(asset));
|
||||
} else if asset
|
||||
.ident()
|
||||
.path()
|
||||
.await?
|
||||
.is_inside(&*client_relative_path.await?)
|
||||
{
|
||||
// Client assets are emitted to the client output path, which is prefixed with
|
||||
// _next. We need to rebase them to remove that prefix.
|
||||
return Ok(emit_rebase(asset, client_relative_path, client_output_path));
|
||||
}
|
||||
|
||||
Ok(CompletionVc::immutable())
|
||||
})
|
||||
.try_join()
|
||||
.await?,
|
||||
))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn emit(asset: OutputAssetVc) -> CompletionVc {
|
||||
asset.content().write(asset.ident().path())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
fn emit_rebase(asset: OutputAssetVc, from: FileSystemPathVc, to: FileSystemPathVc) -> CompletionVc {
|
||||
asset
|
||||
.content()
|
||||
.write(rebase(asset.ident().path(), from, to))
|
||||
}
|
||||
|
||||
/// Walks the asset graph from multiple assets and collect all referenced
|
||||
/// assets.
|
||||
#[turbo_tasks::function]
|
||||
async fn all_assets_from_entries(entries: OutputAssetsVc) -> Result<OutputAssetsVc> {
|
||||
Ok(OutputAssetsVc::cell(
|
||||
AdjacencyMap::new()
|
||||
.skip_duplicates()
|
||||
.visit(entries.await?.iter().copied(), get_referenced_assets)
|
||||
.await
|
||||
.completed()?
|
||||
.into_inner()
|
||||
.into_reverse_topological()
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Computes the list of all chunk children of a given chunk.
|
||||
async fn get_referenced_assets(
|
||||
asset: OutputAssetVc,
|
||||
) -> Result<impl Iterator<Item = OutputAssetVc> + Send> {
|
||||
Ok(asset
|
||||
.references()
|
||||
.await?
|
||||
.iter()
|
||||
.map(|reference| async move {
|
||||
let primary_assets = reference.resolve_reference().primary_assets().await?;
|
||||
Ok(primary_assets.clone_value())
|
||||
})
|
||||
.try_join()
|
||||
.await?
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|asset| async move { Ok(OutputAssetVc::resolve_from(asset).await?) })
|
||||
.try_join()
|
||||
.await?
|
||||
.into_iter()
|
||||
.flatten())
|
||||
}
|
|
@ -11,6 +11,7 @@ pub mod app_structure;
|
|||
mod babel;
|
||||
mod bootstrap;
|
||||
mod embed_js;
|
||||
mod emit;
|
||||
pub mod env;
|
||||
mod fallback;
|
||||
pub mod loader_tree;
|
||||
|
@ -41,10 +42,11 @@ mod runtime;
|
|||
mod sass;
|
||||
mod transform_options;
|
||||
pub mod url_node;
|
||||
mod util;
|
||||
pub mod util;
|
||||
mod web_entry_source;
|
||||
|
||||
pub use app_source::create_app_source;
|
||||
pub use emit::emit_all_assets;
|
||||
pub use next_app::unsupported_dynamic_metadata_issue::{
|
||||
UnsupportedDynamicMetadataIssue, UnsupportedDynamicMetadataIssueVc,
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ use turbopack_binding::{
|
|||
|
||||
use crate::{
|
||||
embed_js::next_js_file,
|
||||
next_config::{NextConfigVc, RewritesReadRef},
|
||||
next_config::{RewritesReadRef, RewritesVc},
|
||||
util::get_asset_path_from_pathname,
|
||||
};
|
||||
|
||||
|
@ -35,7 +35,7 @@ use crate::{
|
|||
#[turbo_tasks::value(shared)]
|
||||
pub struct DevManifestContentSource {
|
||||
pub page_roots: Vec<ContentSourceVc>,
|
||||
pub next_config: NextConfigVc,
|
||||
pub rewrites: RewritesVc,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value_impl]
|
||||
|
@ -124,7 +124,7 @@ impl DevManifestContentSourceVc {
|
|||
.collect();
|
||||
|
||||
let manifest = BuildManifest {
|
||||
rewrites: this.next_config.rewrites().await?,
|
||||
rewrites: this.rewrites.await?,
|
||||
sorted_pages,
|
||||
routes,
|
||||
};
|
||||
|
|
|
@ -45,6 +45,34 @@ use turbopack_binding::{
|
|||
|
||||
use crate::{embed_js::next_asset, next_shared::transforms::ModularizeImportPackageConfig};
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct NextConfigAndCustomRoutesRaw {
|
||||
config: NextConfig,
|
||||
custom_routes: CustomRoutesRaw,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value]
|
||||
struct NextConfigAndCustomRoutes {
|
||||
config: NextConfigVc,
|
||||
custom_routes: CustomRoutesVc,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CustomRoutesRaw {
|
||||
rewrites: Rewrites,
|
||||
|
||||
// unsupported
|
||||
headers: Vec<Header>,
|
||||
redirects: Vec<Redirect>,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value]
|
||||
struct CustomRoutes {
|
||||
rewrites: RewritesVc,
|
||||
}
|
||||
|
||||
#[turbo_tasks::value(serialization = "custom", eq = "manual")]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
@ -57,7 +85,6 @@ pub struct NextConfig {
|
|||
pub images: ImageConfig,
|
||||
pub page_extensions: Vec<String>,
|
||||
pub react_strict_mode: Option<bool>,
|
||||
pub rewrites: Rewrites,
|
||||
pub transpile_packages: Option<Vec<String>>,
|
||||
pub modularize_imports: Option<IndexMap<String, ModularizeImportPackageConfig>>,
|
||||
sass_options: Option<serde_json::Value>,
|
||||
|
@ -84,7 +111,6 @@ pub struct NextConfig {
|
|||
// this is a function in js land
|
||||
generate_build_id: Option<serde_json::Value>,
|
||||
generate_etags: bool,
|
||||
headers: Vec<Header>,
|
||||
http_agent_options: HttpAgentConfig,
|
||||
i18n: Option<I18NConfig>,
|
||||
on_demand_entries: OnDemandEntriesConfig,
|
||||
|
@ -93,7 +119,6 @@ pub struct NextConfig {
|
|||
powered_by_header: bool,
|
||||
production_browser_source_maps: bool,
|
||||
public_runtime_config: IndexMap<String, serde_json::Value>,
|
||||
redirects: Vec<Redirect>,
|
||||
server_runtime_config: IndexMap<String, serde_json::Value>,
|
||||
static_page_generation_timeout: f64,
|
||||
swc_minify: bool,
|
||||
|
@ -486,6 +511,13 @@ pub enum RemoveConsoleConfig {
|
|||
|
||||
#[turbo_tasks::value_impl]
|
||||
impl NextConfigVc {
|
||||
#[turbo_tasks::function]
|
||||
pub fn from_string(s: String) -> Result<Self> {
|
||||
let config: NextConfig = serde_json::from_str(&s)
|
||||
.with_context(|| format!("failed to parse next.config.js: {}", s))?;
|
||||
Ok(config.cell())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub async fn server_component_externals(self) -> Result<StringsVc> {
|
||||
Ok(StringsVc::cell(
|
||||
|
@ -545,11 +577,6 @@ impl NextConfigVc {
|
|||
Ok(StringsVc::cell(self.await?.page_extensions.clone()))
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub async fn rewrites(self) -> Result<RewritesVc> {
|
||||
Ok(self.await?.rewrites.clone().cell())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub async fn transpile_packages(self) -> Result<StringsVc> {
|
||||
Ok(StringsVc::cell(
|
||||
|
@ -646,22 +673,41 @@ fn next_configs() -> StringsVc {
|
|||
|
||||
#[turbo_tasks::function]
|
||||
pub async fn load_next_config(execution_context: ExecutionContextVc) -> Result<NextConfigVc> {
|
||||
Ok(load_config_and_custom_routes(execution_context)
|
||||
.await?
|
||||
.config)
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub async fn load_rewrites(execution_context: ExecutionContextVc) -> Result<RewritesVc> {
|
||||
Ok(load_config_and_custom_routes(execution_context)
|
||||
.await?
|
||||
.custom_routes
|
||||
.await?
|
||||
.rewrites)
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
async fn load_config_and_custom_routes(
|
||||
execution_context: ExecutionContextVc,
|
||||
) -> Result<NextConfigAndCustomRoutesVc> {
|
||||
let ExecutionContext { project_path, .. } = *execution_context.await?;
|
||||
let find_config_result = find_context_file(project_path, next_configs());
|
||||
let config_file = match &*find_config_result.await? {
|
||||
FindContextFileResult::Found(config_path, _) => Some(*config_path),
|
||||
FindContextFileResult::NotFound(_) => None,
|
||||
};
|
||||
load_next_config_internal(execution_context, config_file)
|
||||
|
||||
load_next_config_and_custom_routes_internal(execution_context, config_file)
|
||||
.issue_context(config_file, "Loading Next.js config")
|
||||
.await
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
pub async fn load_next_config_internal(
|
||||
async fn load_next_config_and_custom_routes_internal(
|
||||
execution_context: ExecutionContextVc,
|
||||
config_file: Option<FileSystemPathVc>,
|
||||
) -> Result<NextConfigVc> {
|
||||
) -> Result<NextConfigAndCustomRoutesVc> {
|
||||
let ExecutionContext {
|
||||
project_path,
|
||||
chunking_context,
|
||||
|
@ -709,11 +755,24 @@ pub async fn load_next_config_internal(
|
|||
.await
|
||||
.context("Evaluation of Next.js config failed")?
|
||||
else {
|
||||
return Ok(NextConfig::default().cell());
|
||||
return Ok(NextConfigAndCustomRoutes {
|
||||
config: NextConfig::default().cell(),
|
||||
custom_routes: CustomRoutes {
|
||||
rewrites: Rewrites::default().cell(),
|
||||
}
|
||||
.cell(),
|
||||
}
|
||||
.cell());
|
||||
};
|
||||
let next_config: NextConfig = parse_json_with_source_context(val.to_str()?)?;
|
||||
let next_config_and_custom_routes: NextConfigAndCustomRoutesRaw =
|
||||
parse_json_with_source_context(val.to_str()?)?;
|
||||
|
||||
if let Some(turbo) = next_config.experimental.turbo.as_ref() {
|
||||
if let Some(turbo) = next_config_and_custom_routes
|
||||
.config
|
||||
.experimental
|
||||
.turbo
|
||||
.as_ref()
|
||||
{
|
||||
if turbo.loaders.is_some() {
|
||||
OutdatedConfigIssue {
|
||||
path: config_file.unwrap_or(project_path),
|
||||
|
@ -730,7 +789,14 @@ Example: loaders: { \".mdx\": [\"mdx-loader\"] } -> rules: { \"*.mdx\": [\"mdx-l
|
|||
}
|
||||
}
|
||||
|
||||
Ok(next_config.cell())
|
||||
Ok(NextConfigAndCustomRoutes {
|
||||
config: next_config_and_custom_routes.config.cell(),
|
||||
custom_routes: CustomRoutes {
|
||||
rewrites: next_config_and_custom_routes.custom_routes.rewrites.cell(),
|
||||
}
|
||||
.cell(),
|
||||
}
|
||||
.cell())
|
||||
}
|
||||
|
||||
#[turbo_tasks::function]
|
||||
|
|
|
@ -5,7 +5,7 @@ use turbopack_binding::turbo::tasks_fs::{
|
|||
DirectoryContent, DirectoryEntry, FileSystemEntryType, FileSystemPathVc,
|
||||
};
|
||||
|
||||
use crate::{embed_js::next_js_file_path, next_config::NextConfigVc};
|
||||
use crate::embed_js::next_js_file_path;
|
||||
|
||||
/// A final route in the pages directory.
|
||||
#[turbo_tasks::value]
|
||||
|
@ -125,7 +125,7 @@ impl PagesDirectoryStructureVc {
|
|||
pub async fn find_pages_structure(
|
||||
project_root: FileSystemPathVc,
|
||||
next_router_root: FileSystemPathVc,
|
||||
next_config: NextConfigVc,
|
||||
page_extensions: StringsVc,
|
||||
) -> Result<PagesStructureVc> {
|
||||
let pages_root = project_root.join("pages");
|
||||
let pages_root: FileSystemPathOptionVc = FileSystemPathOptionVc::cell(
|
||||
|
@ -149,7 +149,7 @@ pub async fn find_pages_structure(
|
|||
Ok(get_pages_structure_for_root_directory(
|
||||
pages_root,
|
||||
next_router_root,
|
||||
next_config.page_extensions(),
|
||||
page_extensions,
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ use next_core::{
|
|||
manifest::DevManifestContentSource,
|
||||
mode::NextMode,
|
||||
next_client::{get_client_chunking_context, get_client_compile_time_info},
|
||||
next_config::load_next_config,
|
||||
next_config::{load_next_config, load_rewrites},
|
||||
next_image::NextImageContentSourceVc,
|
||||
pages_structure::find_pages_structure,
|
||||
router_source::NextRouterContentSourceVc,
|
||||
|
@ -335,7 +335,9 @@ async fn source(
|
|||
ExecutionContextVc::new(project_path, build_chunking_context.into(), env);
|
||||
|
||||
let mode = NextMode::Development;
|
||||
let next_config = load_next_config(execution_context.with_layer("next_config"));
|
||||
let next_config_execution_context = execution_context.with_layer("next_config");
|
||||
let next_config = load_next_config(next_config_execution_context);
|
||||
let rewrites = load_rewrites(next_config_execution_context);
|
||||
|
||||
let output_root = output_fs.root().join(".next/server");
|
||||
|
||||
|
@ -367,7 +369,8 @@ async fn source(
|
|||
client_compile_time_info.environment(),
|
||||
mode,
|
||||
);
|
||||
let pages_structure = find_pages_structure(project_path, dev_server_root, next_config);
|
||||
let pages_structure =
|
||||
find_pages_structure(project_path, dev_server_root, next_config.page_extensions());
|
||||
let page_source = create_page_source(
|
||||
pages_structure,
|
||||
project_path,
|
||||
|
@ -402,7 +405,7 @@ async fn source(
|
|||
StaticAssetsContentSourceVc::new(String::new(), project_path.join("public")).into();
|
||||
let manifest_source = DevManifestContentSource {
|
||||
page_roots: vec![page_source],
|
||||
next_config,
|
||||
rewrites,
|
||||
}
|
||||
.cell()
|
||||
.into();
|
||||
|
|
|
@ -939,7 +939,7 @@ export default async function build(
|
|||
ignore: [] as string[],
|
||||
}))
|
||||
|
||||
let binding = (await loadBindings()) as any
|
||||
let binding = await loadBindings()
|
||||
|
||||
async function turbopackBuild() {
|
||||
const turboNextBuildStart = process.hrtime()
|
||||
|
|
|
@ -9,6 +9,8 @@ import { eventSwcLoadFailure } from '../../telemetry/events/swc-load-failure'
|
|||
import { patchIncorrectLockfile } from '../../lib/patch-incorrect-lockfile'
|
||||
import { downloadWasmSwc } from '../../lib/download-wasm-swc'
|
||||
import { spawn } from 'child_process'
|
||||
import { NextConfigComplete, TurboLoaderItem } from '../../server/config-shared'
|
||||
import { isDeepStrictEqual } from 'util'
|
||||
|
||||
const nextVersion = process.env.__NEXT_VERSION as string
|
||||
|
||||
|
@ -88,7 +90,38 @@ let swcHeapProfilerFlushGuard: any
|
|||
let swcCrashReporterFlushGuard: any
|
||||
export const lockfilePatchPromise: { cur?: Promise<void> } = {}
|
||||
|
||||
export async function loadBindings(): Promise<any> {
|
||||
export interface Binding {
|
||||
isWasm: boolean
|
||||
turbo: {
|
||||
startDev: any
|
||||
startTrace: any
|
||||
nextBuild?: any
|
||||
createTurboTasks?: any
|
||||
entrypoints: {
|
||||
stream: any
|
||||
get: any
|
||||
}
|
||||
mdx: {
|
||||
compile: any
|
||||
compileSync: any
|
||||
}
|
||||
createProject: (options: ProjectOptions) => Promise<Project>
|
||||
}
|
||||
minify: any
|
||||
minifySync: any
|
||||
transform: any
|
||||
transformSync: any
|
||||
parse: any
|
||||
parseSync: any
|
||||
getTargetTriple(): string | undefined
|
||||
initCustomTraceSubscriber?: any
|
||||
teardownTraceSubscriber?: any
|
||||
initHeapProfiler?: any
|
||||
teardownHeapProfiler?: any
|
||||
teardownCrashReporter?: any
|
||||
}
|
||||
|
||||
export async function loadBindings(): Promise<Binding> {
|
||||
if (pendingBindings) {
|
||||
return pendingBindings
|
||||
}
|
||||
|
@ -236,6 +269,379 @@ function logLoadFailure(attempts: any, triedWasm = false) {
|
|||
})
|
||||
}
|
||||
|
||||
interface ProjectOptions {
|
||||
/**
|
||||
* A root path from which all files must be nested under. Trying to access
|
||||
* a file outside this root will fail. Think of this as a chroot.
|
||||
*/
|
||||
rootPath: string
|
||||
|
||||
/**
|
||||
* A path inside the root_path which contains the app/pages directories.
|
||||
*/
|
||||
projectPath: string
|
||||
|
||||
/**
|
||||
* The next.config.js contents.
|
||||
*/
|
||||
nextConfig: NextConfigComplete
|
||||
|
||||
/**
|
||||
* Whether to watch he filesystem for file changes.
|
||||
*/
|
||||
watch: boolean
|
||||
|
||||
/**
|
||||
* An upper bound of memory that turbopack will attempt to stay under.
|
||||
*/
|
||||
memoryLimit?: number
|
||||
}
|
||||
|
||||
interface Issue {}
|
||||
|
||||
interface Diagnostics {}
|
||||
|
||||
type TurbopackResult<T = {}> = T & {
|
||||
issues: Issue[]
|
||||
diagnostics: Diagnostics[]
|
||||
}
|
||||
|
||||
interface Middleware {
|
||||
endpoint: Endpoint
|
||||
runtime: 'nodejs' | 'edge'
|
||||
matcher?: string[]
|
||||
}
|
||||
|
||||
interface Entrypoints {
|
||||
routes: Map<string, Route>
|
||||
middleware?: Middleware
|
||||
}
|
||||
|
||||
interface Project {
|
||||
entrypointsSubscribe(): AsyncIterableIterator<TurbopackResult<Entrypoints>>
|
||||
}
|
||||
|
||||
type Route =
|
||||
| {
|
||||
type: 'conflict'
|
||||
}
|
||||
| {
|
||||
type: 'app-page'
|
||||
htmlEndpoint: Endpoint
|
||||
rscEndpoint: Endpoint
|
||||
}
|
||||
| {
|
||||
type: 'app-route'
|
||||
endpoint: Endpoint
|
||||
}
|
||||
| {
|
||||
type: 'page'
|
||||
htmlEndpoint: Endpoint
|
||||
dataEndpoint: Endpoint
|
||||
}
|
||||
| {
|
||||
type: 'page-api'
|
||||
endpoint: Endpoint
|
||||
}
|
||||
|
||||
interface Endpoint {
|
||||
/** Write files for the endpoint to disk. */
|
||||
writeToDisk(): Promise<TurbopackResult<WrittenEndpoint>>
|
||||
/**
|
||||
* Listen to changes to the endpoint.
|
||||
* After changed() has been awaited it will listen to changes.
|
||||
* The async iterator will yield for each change.
|
||||
*/
|
||||
changed(): Promise<AsyncIterableIterator<TurbopackResult>>
|
||||
}
|
||||
|
||||
interface EndpointConfig {
|
||||
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
|
||||
dynamicParams?: boolean
|
||||
revalidate?: 'never' | 'force-cache' | number
|
||||
fetchCache?:
|
||||
| 'auto'
|
||||
| 'default-cache'
|
||||
| 'only-cache'
|
||||
| 'force-cache'
|
||||
| 'default-no-store'
|
||||
| 'only-no-store'
|
||||
| 'force-no-store'
|
||||
runtime?: 'nodejs' | 'edge'
|
||||
preferredRegion?: string
|
||||
}
|
||||
|
||||
interface WrittenEndpoint {
|
||||
/** The entry path for the endpoint. */
|
||||
entryPath: string
|
||||
/** All paths that has been written for the endpoint. */
|
||||
paths: string[]
|
||||
config: EndpointConfig
|
||||
}
|
||||
|
||||
// TODO(sokra) Support wasm option.
|
||||
function bindingToApi(binding: any, _wasm: boolean) {
|
||||
type NativeFunction<T> = (
|
||||
callback: (err: Error, value: T) => void
|
||||
) => Promise<{ __napiType: 'RootTask' }>
|
||||
|
||||
/**
|
||||
* Utility function to ensure all variants of an enum are handled.
|
||||
*/
|
||||
function invariant(
|
||||
never: never,
|
||||
computeMessage: (arg: any) => string
|
||||
): never {
|
||||
throw new Error(`Invariant: ${computeMessage(never)}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a native function and streams the result.
|
||||
* If useBuffer is true, all values will be preserved, potentially buffered
|
||||
* if consumed slower than produced. Else, only the latest value will be
|
||||
* preserved.
|
||||
*/
|
||||
function subscribe<T>(
|
||||
useBuffer: boolean,
|
||||
nativeFunction: NativeFunction<T>
|
||||
): AsyncIterableIterator<T> {
|
||||
type BufferItem =
|
||||
| { err: Error; value: undefined }
|
||||
| { err: undefined; value: T }
|
||||
// A buffer of produced items. This will only contain values if the
|
||||
// consumer is slower than the producer.
|
||||
let buffer: BufferItem[] = []
|
||||
// A deferred value waiting for the next produced item. This will only
|
||||
// exist if the consumer is faster than the producer.
|
||||
let waiting:
|
||||
| {
|
||||
resolve: (value: T) => void
|
||||
reject: (error: Error) => void
|
||||
}
|
||||
| undefined
|
||||
|
||||
// The native function will call this every time it emits a new result. We
|
||||
// either need to notify a waiting consumer, or buffer the new result until
|
||||
// the consumer catches up.
|
||||
const emitResult = (err: Error | undefined, value: T | undefined) => {
|
||||
if (waiting) {
|
||||
let { resolve, reject } = waiting
|
||||
waiting = undefined
|
||||
if (err) reject(err)
|
||||
else resolve(value!)
|
||||
} else {
|
||||
const item = { err, value } as BufferItem
|
||||
if (useBuffer) buffer.push(item)
|
||||
else buffer[0] = item
|
||||
}
|
||||
}
|
||||
|
||||
return (async function* () {
|
||||
const task = await nativeFunction(emitResult)
|
||||
try {
|
||||
while (true) {
|
||||
if (buffer.length > 0) {
|
||||
const item = buffer.shift()!
|
||||
if (item.err) throw item.err
|
||||
yield item.value
|
||||
} else {
|
||||
// eslint-disable-next-line no-loop-func
|
||||
yield new Promise<T>((resolve, reject) => {
|
||||
waiting = { resolve, reject }
|
||||
})
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
binding.rootTaskDispose(task)
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
class ProjectImpl implements Project {
|
||||
private _nativeProject: { __napiType: 'Project' }
|
||||
|
||||
constructor(nativeProject: { __napiType: 'Project' }) {
|
||||
this._nativeProject = nativeProject
|
||||
}
|
||||
|
||||
entrypointsSubscribe() {
|
||||
type NapiEndpoint = { __napiType: 'Endpoint' }
|
||||
|
||||
type NapiEntrypoints = {
|
||||
routes: NapiRoute[]
|
||||
middleware?: NapiMiddleware
|
||||
issues: Issue[]
|
||||
diagnostics: Diagnostics[]
|
||||
}
|
||||
|
||||
type NapiMiddleware = {
|
||||
endpoint: NapiEndpoint
|
||||
runtime: 'nodejs' | 'edge'
|
||||
matcher?: string[]
|
||||
}
|
||||
|
||||
type NapiRoute = {
|
||||
pathname: string
|
||||
} & (
|
||||
| {
|
||||
type: 'page'
|
||||
htmlEndpoint: NapiEndpoint
|
||||
dataEndpoint: NapiEndpoint
|
||||
}
|
||||
| {
|
||||
type: 'page-api'
|
||||
endpoint: NapiEndpoint
|
||||
}
|
||||
| {
|
||||
type: 'app-page'
|
||||
htmlEndpoint: NapiEndpoint
|
||||
rscEndpoint: NapiEndpoint
|
||||
}
|
||||
| {
|
||||
type: 'app-route'
|
||||
endpoint: NapiEndpoint
|
||||
}
|
||||
| {
|
||||
type: 'conflict'
|
||||
}
|
||||
)
|
||||
|
||||
const subscription = subscribe<NapiEntrypoints>(false, async (callback) =>
|
||||
binding.projectEntrypointsSubscribe(await this._nativeProject, callback)
|
||||
)
|
||||
return (async function* () {
|
||||
for await (const entrypoints of subscription) {
|
||||
const routes = new Map()
|
||||
for (const { pathname, ...nativeRoute } of entrypoints.routes) {
|
||||
let route: Route
|
||||
switch (nativeRoute.type) {
|
||||
case 'page':
|
||||
route = {
|
||||
type: 'page',
|
||||
htmlEndpoint: new EndpointImpl(nativeRoute.htmlEndpoint),
|
||||
dataEndpoint: new EndpointImpl(nativeRoute.dataEndpoint),
|
||||
}
|
||||
break
|
||||
case 'page-api':
|
||||
route = {
|
||||
type: 'page-api',
|
||||
endpoint: new EndpointImpl(nativeRoute.endpoint),
|
||||
}
|
||||
break
|
||||
case 'app-page':
|
||||
route = {
|
||||
type: 'app-page',
|
||||
htmlEndpoint: new EndpointImpl(nativeRoute.htmlEndpoint),
|
||||
rscEndpoint: new EndpointImpl(nativeRoute.rscEndpoint),
|
||||
}
|
||||
break
|
||||
case 'app-route':
|
||||
route = {
|
||||
type: 'app-route',
|
||||
endpoint: new EndpointImpl(nativeRoute.endpoint),
|
||||
}
|
||||
break
|
||||
case 'conflict':
|
||||
route = {
|
||||
type: 'conflict',
|
||||
}
|
||||
break
|
||||
default:
|
||||
invariant(
|
||||
nativeRoute,
|
||||
() => `Unknown route type: ${(nativeRoute as any).type}`
|
||||
)
|
||||
break
|
||||
}
|
||||
routes.set(pathname, route)
|
||||
}
|
||||
const napiMiddlewareToMiddleware = (middleware: NapiMiddleware) => ({
|
||||
endpoint: new EndpointImpl(middleware.endpoint),
|
||||
runtime: middleware.runtime,
|
||||
matcher: middleware.matcher,
|
||||
})
|
||||
const middleware = entrypoints.middleware
|
||||
? napiMiddlewareToMiddleware(entrypoints.middleware)
|
||||
: undefined
|
||||
yield {
|
||||
routes,
|
||||
middleware,
|
||||
issues: entrypoints.issues,
|
||||
diagnostics: entrypoints.diagnostics,
|
||||
}
|
||||
}
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
class EndpointImpl implements Endpoint {
|
||||
private _nativeEndpoint: { __napiType: 'Endpoint' }
|
||||
|
||||
constructor(nativeEndpoint: { __napiType: 'Endpoint' }) {
|
||||
this._nativeEndpoint = nativeEndpoint
|
||||
}
|
||||
|
||||
async writeToDisk(): Promise<TurbopackResult<WrittenEndpoint>> {
|
||||
return await binding.endpointWriteToDisk(this._nativeEndpoint)
|
||||
}
|
||||
|
||||
async changed(): Promise<AsyncIterableIterator<TurbopackResult>> {
|
||||
const iter = subscribe<TurbopackResult>(false, async (callback) =>
|
||||
binding.endpointChangedSubscribe(await this._nativeEndpoint, callback)
|
||||
)
|
||||
await iter.next()
|
||||
return iter
|
||||
}
|
||||
}
|
||||
|
||||
async function serializeNextConfig(
|
||||
nextConfig: NextConfigComplete
|
||||
): Promise<string> {
|
||||
let nextConfigSerializable = nextConfig as any
|
||||
|
||||
nextConfigSerializable.generateBuildId =
|
||||
await nextConfig.generateBuildId?.()
|
||||
|
||||
// TODO: these functions takes arguments, have to be supported in a different way
|
||||
nextConfigSerializable.exportPathMap = {}
|
||||
nextConfigSerializable.webpack = nextConfig.webpack && {}
|
||||
|
||||
if (nextConfig.experimental?.turbo?.loaders) {
|
||||
ensureLoadersHaveSerializableOptions(
|
||||
nextConfig.experimental.turbo.loaders
|
||||
)
|
||||
}
|
||||
|
||||
return JSON.stringify(nextConfigSerializable)
|
||||
}
|
||||
|
||||
function ensureLoadersHaveSerializableOptions(
|
||||
turbopackLoaders: Record<string, TurboLoaderItem[]>
|
||||
) {
|
||||
for (const [ext, loaderItems] of Object.entries(turbopackLoaders)) {
|
||||
for (const loaderItem of loaderItems) {
|
||||
if (
|
||||
typeof loaderItem !== 'string' &&
|
||||
!isDeepStrictEqual(loaderItem, JSON.parse(JSON.stringify(loaderItem)))
|
||||
) {
|
||||
throw new Error(
|
||||
`loader ${loaderItem.loader} for match "${ext}" does not have serializable options. Ensure that options passed are plain JavaScript objects and values.`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function createProject(options: ProjectOptions) {
|
||||
const optionsForRust = options as any
|
||||
optionsForRust.nextConfig = await serializeNextConfig(options.nextConfig)
|
||||
|
||||
return new ProjectImpl(await binding.projectNew(optionsForRust))
|
||||
}
|
||||
|
||||
return createProject
|
||||
}
|
||||
|
||||
async function loadWasm(importPath = '', isCustomTurbopack: boolean) {
|
||||
if (wasmBindings) {
|
||||
return wasmBindings
|
||||
|
@ -314,9 +720,6 @@ async function loadWasm(importPath = '', isCustomTurbopack: boolean) {
|
|||
startTrace: () => {
|
||||
Log.error('Wasm binding does not support trace yet')
|
||||
},
|
||||
experimentalTurbo: () => {
|
||||
Log.error('Wasm binding does not support this interface')
|
||||
},
|
||||
entrypoints: {
|
||||
stream: (
|
||||
turboTasks: any,
|
||||
|
@ -577,12 +980,6 @@ function loadNative(isCustomTurbopack = false) {
|
|||
)
|
||||
return ret
|
||||
},
|
||||
experimentalTurbo: () => {
|
||||
initHeapProfiler()
|
||||
|
||||
const ret = bindings.experimentalTurbo()
|
||||
return ret
|
||||
},
|
||||
createTurboTasks: (memoryLimit?: number): unknown =>
|
||||
bindings.createTurboTasks(memoryLimit),
|
||||
entrypoints: {
|
||||
|
@ -615,6 +1012,7 @@ function loadNative(isCustomTurbopack = false) {
|
|||
)
|
||||
},
|
||||
},
|
||||
createProject: bindingToApi(bindings, false),
|
||||
},
|
||||
mdx: {
|
||||
compile: (src: string, options: any) =>
|
||||
|
|
|
@ -218,7 +218,7 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance {
|
|||
|
||||
// startTrace existed and callable
|
||||
if (this.turbotrace) {
|
||||
let binding = (await loadBindings()) as any
|
||||
let binding = await loadBindings()
|
||||
if (
|
||||
!binding?.isWasm &&
|
||||
typeof binding.turbo.startTrace === 'function'
|
||||
|
@ -475,7 +475,7 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance {
|
|||
})
|
||||
// startTrace existed and callable
|
||||
if (this.turbotrace) {
|
||||
let binding = (await loadBindings()) as any
|
||||
let binding = await loadBindings()
|
||||
if (
|
||||
!binding?.isWasm &&
|
||||
typeof binding.turbo.startTrace === 'function'
|
||||
|
@ -825,7 +825,7 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance {
|
|||
|
||||
if (this.turbotrace) {
|
||||
compiler.hooks.afterEmit.tapPromise(PLUGIN_NAME, async () => {
|
||||
let binding = (await loadBindings()) as any
|
||||
let binding = await loadBindings()
|
||||
if (
|
||||
!binding?.isWasm &&
|
||||
typeof binding.turbo.startTrace === 'function'
|
||||
|
|
|
@ -211,7 +211,6 @@ const nextDev: CliCommand = async (argv) => {
|
|||
// We do not set a default host value here to prevent breaking
|
||||
// some set-ups that rely on listening on other interfaces
|
||||
const host = args['--hostname']
|
||||
const experimentalTurbo = args['--experimental-turbo']
|
||||
|
||||
const devServerOptions: StartServerOptions = {
|
||||
dir,
|
||||
|
@ -221,14 +220,64 @@ const nextDev: CliCommand = async (argv) => {
|
|||
hostname: host,
|
||||
// This is required especially for app dir.
|
||||
useWorkers: true,
|
||||
isExperimentalTurbo: experimentalTurbo,
|
||||
}
|
||||
|
||||
if (args['--turbo']) {
|
||||
process.env.TURBOPACK = '1'
|
||||
}
|
||||
if (args['--experimental-turbo']) {
|
||||
process.env.EXPERIMENTAL_TURBOPACK = '1'
|
||||
}
|
||||
const experimentalTurbo = !!process.env.EXPERIMENTAL_TURBOPACK
|
||||
|
||||
if (process.env.TURBOPACK) {
|
||||
if (experimentalTurbo) {
|
||||
const { loadBindings } =
|
||||
require('../build/swc') as typeof import('../build/swc')
|
||||
|
||||
resetEnv()
|
||||
let bindings = await loadBindings()
|
||||
|
||||
const config = await loadConfig(
|
||||
PHASE_DEVELOPMENT_SERVER,
|
||||
dir,
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
)
|
||||
|
||||
// Just testing code here:
|
||||
|
||||
const project = await bindings.turbo.createProject({
|
||||
projectPath: dir,
|
||||
rootPath: dir,
|
||||
nextConfig: config,
|
||||
watch: true,
|
||||
})
|
||||
const iter = project.entrypointsSubscribe()
|
||||
|
||||
try {
|
||||
for await (const entrypoints of iter) {
|
||||
for (const [pathname, route] of entrypoints.routes) {
|
||||
switch (route.type) {
|
||||
case 'page': {
|
||||
Log.info(`writing ${pathname} to disk`)
|
||||
const written = await route.htmlEndpoint.writeToDisk()
|
||||
Log.info(written)
|
||||
break
|
||||
}
|
||||
default:
|
||||
Log.info(`skipping ${pathname} (${route.type})`)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
Log.error('Not supported yet')
|
||||
process.exit(1)
|
||||
} else if (process.env.TURBOPACK) {
|
||||
isTurboSession = true
|
||||
|
||||
const { validateTurboNextConfig } =
|
||||
|
@ -297,7 +346,8 @@ const nextDev: CliCommand = async (argv) => {
|
|||
// Turbopack need to be in control over reading the .env files and watching them.
|
||||
// So we need to start with a initial env to know which env vars are coming from the user.
|
||||
resetEnv()
|
||||
let bindings: any = await loadBindings()
|
||||
let bindings = await loadBindings()
|
||||
|
||||
let server = bindings.turbo.startDev({
|
||||
...devServerOptions,
|
||||
showAll: args['--show-all'] ?? false,
|
||||
|
@ -318,11 +368,6 @@ const nextDev: CliCommand = async (argv) => {
|
|||
|
||||
return server
|
||||
} else {
|
||||
if (experimentalTurbo) {
|
||||
Log.error('Not supported yet')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
let cleanupFns: (() => Promise<void> | void)[] = []
|
||||
const runDevServer = async () => {
|
||||
const oldCleanupFns = cleanupFns
|
||||
|
|
|
@ -85,7 +85,7 @@ type JSONValue =
|
|||
| JSONValue[]
|
||||
| { [k: string]: JSONValue }
|
||||
|
||||
type TurboLoaderItem =
|
||||
export type TurboLoaderItem =
|
||||
| string
|
||||
| {
|
||||
loader: string
|
||||
|
@ -93,7 +93,7 @@ type TurboLoaderItem =
|
|||
options: Record<string, JSONValue>
|
||||
}
|
||||
|
||||
interface ExperimentalTurboOptions {
|
||||
export interface ExperimentalTurboOptions {
|
||||
/**
|
||||
* (`next --turbo` only) A mapping of aliased imports to modules to load in their place.
|
||||
*
|
||||
|
|
|
@ -22,7 +22,6 @@ export interface StartServerOptions {
|
|||
useWorkers: boolean
|
||||
allowRetry?: boolean
|
||||
isTurbopack?: boolean
|
||||
isExperimentalTurbo?: boolean
|
||||
keepAliveTimeout?: number
|
||||
onStdout?: (data: any) => void
|
||||
onStderr?: (data: any) => void
|
||||
|
|
|
@ -992,8 +992,8 @@ importers:
|
|||
'@types/react': 18.2.7
|
||||
'@types/react-dom': 18.2.4
|
||||
'@vercel/ncc': ^0.36.0
|
||||
'@vercel/turbopack-ecmascript-runtime': https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.2
|
||||
'@vercel/turbopack-node': https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230713.2
|
||||
'@vercel/turbopack-ecmascript-runtime': https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.3
|
||||
'@vercel/turbopack-node': https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230713.3
|
||||
anser: ^2.1.1
|
||||
css.escape: ^1.5.1
|
||||
find-up: ^6.3.0
|
||||
|
@ -1005,8 +1005,8 @@ importers:
|
|||
stacktrace-parser: ^0.1.10
|
||||
strip-ansi: ^7.0.1
|
||||
dependencies:
|
||||
'@vercel/turbopack-ecmascript-runtime': '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.2_react-refresh@0.12.0'
|
||||
'@vercel/turbopack-node': '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230713.2'
|
||||
'@vercel/turbopack-ecmascript-runtime': '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.3_react-refresh@0.12.0'
|
||||
'@vercel/turbopack-node': '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230713.3'
|
||||
anser: 2.1.1
|
||||
css.escape: 1.5.1
|
||||
next: link:../../../../next
|
||||
|
@ -25511,9 +25511,9 @@ packages:
|
|||
/zwitch/2.0.4:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
|
||||
'@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.2_react-refresh@0.12.0':
|
||||
resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.2}
|
||||
id: '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.2'
|
||||
'@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.3_react-refresh@0.12.0':
|
||||
resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.3}
|
||||
id: '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230713.3'
|
||||
name: '@vercel/turbopack-ecmascript-runtime'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
|
@ -25524,8 +25524,8 @@ packages:
|
|||
- webpack
|
||||
dev: false
|
||||
|
||||
'@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230713.2':
|
||||
resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230713.2}
|
||||
'@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230713.3':
|
||||
resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-node/js?turbopack-230713.3}
|
||||
name: '@vercel/turbopack-node'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
|
|
Loading…
Reference in a new issue