diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index 6f2112d3e40ca1..d3781cf285419f 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -22,7 +22,7 @@ jobs: # canary next-swc binaries in the monorepo NEXT_SKIP_NATIVE_POSTINSTALL: 1 outputs: - isRelease: ${{ github.event_name != 'workflow_dispatch' && steps.check-release.outputs.IS_RELEASE }} + isRelease: ${{ steps.check-release.outputs.IS_RELEASE }} steps: - name: Setup node uses: actions/setup-node@v4 diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index e3177a3ca5d00d..d961f147952266 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -22,6 +22,11 @@ on: - minor - major + force: + description: create a new release even if there are no new commits + default: false + type: boolean + secrets: RELEASE_BOT_GITHUB_TOKEN: required: true @@ -66,7 +71,7 @@ jobs: run: echo "LATEST_COMMIT=$(git rev-parse HEAD)" >> $GITHUB_ENV - name: Check if new commits since last tag - if: ${{ github.event.inputs.releaseType != 'stable' }} + if: ${{ github.event.inputs.releaseType != 'stable' && github.event.inputs.force != true }} run: | if [ "$LATEST_TAG_COMMIT" = "$LATEST_COMMIT" ]; then echo "No new commits. Exiting..." diff --git a/Cargo.lock b/Cargo.lock index fe973589ac8337..982b6352198353 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -321,7 +321,7 @@ dependencies = [ [[package]] name = "auto-hash-map" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "serde", "smallvec", @@ -1138,7 +1138,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.11.2", + "phf 0.10.1", "serde", "smallvec", ] @@ -3092,7 +3092,7 @@ dependencies = [ [[package]] name = "node-file-trace" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "serde", @@ -3568,7 +3568,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ + "phf_macros 0.10.0", "phf_shared 0.10.0", + "proc-macro-hack", ] [[package]] @@ -3577,7 +3579,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ - "phf_macros", + "phf_macros 0.11.2", "phf_shared 0.11.2", ] @@ -3611,6 +3613,20 @@ dependencies = [ "rand", ] +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "phf_macros" version = "0.11.2" @@ -3805,6 +3821,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" version = "1.0.79" @@ -5018,9 +5040,9 @@ dependencies = [ [[package]] name = "styled_jsx" -version = "0.73.22" +version = "0.73.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05e5aea6dec71fcebfe01fc22139bda1a9a31feff17c219b1e2cee6ce815060" +checksum = "17e38b2334f6613c9e811cc776bc9d2329c288f4dc125df68615fe6cf19b48ae" dependencies = [ "anyhow", "lightningcss", @@ -5278,9 +5300,9 @@ dependencies = [ [[package]] name = "swc_core" -version = "0.92.10" +version = "0.92.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86da1fa18605829228820c41795eb7289eb26542cc62ced2f420bb964d4303ac" +checksum = "e317f6f8b15019358d1e48631c0e6d098d9a3d00d666ea99650201661abea855" dependencies = [ "binding_macros", "swc", @@ -5392,9 +5414,9 @@ dependencies = [ [[package]] name = "swc_css_modules" -version = "0.29.36" +version = "0.29.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b20af192df5adddac04293b5072cc00befa2d6818a9fc90ac6f5c2c49e82dd1c" +checksum = "33a367c7ec6afd24bb3fcc2df95a2adf5d7462367d5b13afd8e43a7beba44358" dependencies = [ "rustc-hash", "serde", @@ -5466,9 +5488,9 @@ dependencies = [ [[package]] name = "swc_ecma_ast" -version = "0.113.6" +version = "0.113.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae3fb68e165bb093ea05fe68dfbc5d378c8b41515a5160f733d7b45bfb9d96e" +checksum = "dc1690cc0c9ab60b44ac0225ba1e231ac532f7ba1d754df761c6ee607561afae" dependencies = [ "bitflags 2.5.0", "bytecheck", @@ -5486,9 +5508,9 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "0.149.2" +version = "0.149.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab6d5e7bbd9208f980b5dad2a4a6ae798c97569f809a48c3f92e6ae7e183c6c" +checksum = "4fef147127a2926ca26171c7afcbf028ff86dc543ced87d316713f25620a15b9" dependencies = [ "memchr", "num-bigint", @@ -5547,9 +5569,9 @@ dependencies = [ [[package]] name = "swc_ecma_compat_es2015" -version = "0.5.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0d3d5d4637af5195265444b2a708020ee90973008ec50c665dad83dd5f1c70" +checksum = "248532f9ae603be6bf4763f66f74ad0dfd82d6307be876ccf4c5d081826a1161" dependencies = [ "arrayvec", "indexmap 2.2.6", @@ -5711,9 +5733,9 @@ dependencies = [ [[package]] name = "swc_ecma_ext_transforms" -version = "0.114.2" +version = "0.114.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91b55ddf8b600f07d0086a9a782d55aa048d3c1ac5eabaa27733d9f45d960e52" +checksum = "259b7b69630aafde63c6304eeacb93fd54619cbdb199c978549acc76cd512d76" dependencies = [ "phf 0.11.2", "swc_atoms", @@ -5767,9 +5789,9 @@ dependencies = [ [[package]] name = "swc_ecma_minifier" -version = "0.194.9" +version = "0.194.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de97742f8d94e7ba458d3d5b5e7d5a7a0349ba7c67819564eb3ab83031307ea6" +checksum = "4dbee669d44953537b6dcaad4a07aa00034fb9eabe4974b5b60acdd1fa9ce209" dependencies = [ "arrayvec", "indexmap 2.2.6", @@ -5801,9 +5823,9 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "0.144.2" +version = "0.144.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31adf4599e8de70f3b754dfc34ec2ab09fa6841d79a9f4a888250a404eae7030" +checksum = "0499e69683ae5d67a20ff0279b94bc90f29df7922a46331b54d5dd367bf89570" dependencies = [ "either", "new_debug_unreachable", @@ -5898,9 +5920,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "0.138.3" +version = "0.138.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38c6d12c8fd704cee66d93038dae2ceb26df18229166d2bdc1ebbb4854d3b36" +checksum = "eddb95c2bdad1c9c29edf35712e1e0f9b9ddc1cdb5ba2d582fd93468cb075a03" dependencies = [ "better_scoped_tls", "bitflags 2.5.0", @@ -6012,9 +6034,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_optimization" -version = "0.199.2" +version = "0.199.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25982d69c91cd64cbfae714d9e953810b3f2835486d08108967cbd15016e7720" +checksum = "32ea30b3df748236c619409f222f0ba68ebeebc08dfff109d2195664a15689f9" dependencies = [ "dashmap", "indexmap 2.2.6", @@ -6125,9 +6147,9 @@ dependencies = [ [[package]] name = "swc_ecma_usage_analyzer" -version = "0.24.3" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7d7109b3794756cc51e842dbb874d2da44293b06a9e3837b477300b0ccef8e" +checksum = "98a693898bd44782a234d9a4122d52b93accf447282d08c2364eb739ae864154" dependencies = [ "indexmap 2.2.6", "rustc-hash", @@ -6142,16 +6164,15 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "0.128.3" +version = "0.128.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f470d8cc31adf6189b228636201ee3cdd268c0b5a2d0407f83093dfa96ff91" +checksum = "fe5242670bc74e0a0b64b9d4912b37be36944517ce0881314162aeb4381272c3" dependencies = [ "indexmap 2.2.6", "num_cpus", "once_cell", "rayon", "rustc-hash", - "ryu-js", "swc_atoms", "swc_common", "swc_ecma_ast", @@ -6311,9 +6332,9 @@ dependencies = [ [[package]] name = "swc_plugin_runner" -version = "0.107.2" +version = "0.107.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc31ec32964d3ebaebfd5a2466a2aaa909aa00722d677f89994b2b6c27d105c" +checksum = "73640537e0967a88a537c853de4a41ba6cdf77bfff1999f7c6c449e5bc550eed" dependencies = [ "anyhow", "enumset", @@ -6948,12 +6969,12 @@ dependencies = [ [[package]] name = "turbo-prehash" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" [[package]] name = "turbo-tasks" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "async-trait", @@ -6985,7 +7006,7 @@ dependencies = [ [[package]] name = "turbo-tasks-build" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "cargo-lock", @@ -6997,7 +7018,7 @@ dependencies = [ [[package]] name = "turbo-tasks-bytes" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "bytes", @@ -7011,7 +7032,7 @@ dependencies = [ [[package]] name = "turbo-tasks-env" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "dotenvs", @@ -7025,7 +7046,7 @@ dependencies = [ [[package]] name = "turbo-tasks-fetch" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "lazy_static", @@ -7041,7 +7062,7 @@ dependencies = [ [[package]] name = "turbo-tasks-fs" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "auto-hash-map", @@ -7073,7 +7094,7 @@ dependencies = [ [[package]] name = "turbo-tasks-hash" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "md4", "turbo-tasks-macros", @@ -7083,7 +7104,7 @@ dependencies = [ [[package]] name = "turbo-tasks-macros" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "proc-macro-error", @@ -7097,7 +7118,7 @@ dependencies = [ [[package]] name = "turbo-tasks-macros-shared" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "proc-macro2", "quote", @@ -7107,7 +7128,7 @@ dependencies = [ [[package]] name = "turbo-tasks-malloc" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "mimalloc", ] @@ -7115,7 +7136,7 @@ dependencies = [ [[package]] name = "turbo-tasks-memory" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "auto-hash-map", @@ -7142,7 +7163,7 @@ dependencies = [ [[package]] name = "turbopack" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "async-recursion", @@ -7172,7 +7193,7 @@ dependencies = [ [[package]] name = "turbopack-binding" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "auto-hash-map", "mdxjs", @@ -7213,7 +7234,7 @@ dependencies = [ [[package]] name = "turbopack-browser" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "indexmap 1.9.3", @@ -7236,7 +7257,7 @@ dependencies = [ [[package]] name = "turbopack-cli-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "clap", @@ -7253,7 +7274,7 @@ dependencies = [ [[package]] name = "turbopack-core" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "async-recursion", @@ -7282,7 +7303,7 @@ dependencies = [ [[package]] name = "turbopack-css" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "indexmap 1.9.3", @@ -7309,7 +7330,7 @@ dependencies = [ [[package]] name = "turbopack-dev-server" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "async-compression", @@ -7345,7 +7366,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "async-trait", @@ -7380,7 +7401,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript-hmr-protocol" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "serde", "serde_json", @@ -7391,7 +7412,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript-plugins" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "async-trait", @@ -7416,7 +7437,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript-runtime" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "indoc", @@ -7432,7 +7453,7 @@ dependencies = [ [[package]] name = "turbopack-env" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "indexmap 1.9.3", @@ -7448,7 +7469,7 @@ dependencies = [ [[package]] name = "turbopack-image" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "base64 0.21.4", @@ -7467,7 +7488,7 @@ dependencies = [ [[package]] name = "turbopack-json" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "serde", @@ -7482,7 +7503,7 @@ dependencies = [ [[package]] name = "turbopack-mdx" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "mdxjs", @@ -7497,7 +7518,7 @@ dependencies = [ [[package]] name = "turbopack-node" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "async-stream", @@ -7531,7 +7552,7 @@ dependencies = [ [[package]] name = "turbopack-nodejs" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "indexmap 1.9.3", @@ -7551,7 +7572,7 @@ dependencies = [ [[package]] name = "turbopack-resolve" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "indexmap 1.9.3", @@ -7569,7 +7590,7 @@ dependencies = [ [[package]] name = "turbopack-static" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "serde", @@ -7585,7 +7606,7 @@ dependencies = [ [[package]] name = "turbopack-swc-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "swc_core", "turbo-tasks", @@ -7596,7 +7617,7 @@ dependencies = [ [[package]] name = "turbopack-trace-server" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "either", @@ -7616,7 +7637,7 @@ dependencies = [ [[package]] name = "turbopack-trace-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "crossbeam-channel", @@ -7632,7 +7653,7 @@ dependencies = [ [[package]] name = "turbopack-wasm" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.1#7a21698bcfb1d64b4b1813071c240eb8b5470724" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-240607.3#c2fb13e3101ae2a6331453a3e925d832e52ced91" dependencies = [ "anyhow", "indexmap 1.9.3", diff --git a/Cargo.toml b/Cargo.toml index 9b6ebafa7d4448..48b55d8bee1224 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,18 +30,18 @@ next-core = { path = "packages/next-swc/crates/next-core" } next-custom-transforms = { path = "packages/next-swc/crates/next-custom-transforms" } # SWC crates -swc_core = { version = "0.92.10", features = [ +swc_core = { version = "0.92.5", features = [ "ecma_loader_lru", "ecma_loader_parking_lot", ] } testing = { version = "0.35.25" } # Turbo crates -turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-240607.1" } +turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-240607.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-240607.1" } +turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-240607.3" } # [TODO]: need to refactor embed_directory! macro usage in next-core -turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-240607.1" } +turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-240607.3" } # General Deps diff --git a/lerna.json b/lerna.json index ed01172966db67..0d8e13d3fc9334 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "15.0.0-canary.17" + "version": "15.0.0-canary.22" } diff --git a/packages/create-next-app/helpers/examples.ts b/packages/create-next-app/helpers/examples.ts index 54bcec0ba453d0..d929981d1d1170 100644 --- a/packages/create-next-app/helpers/examples.ts +++ b/packages/create-next-app/helpers/examples.ts @@ -1,5 +1,5 @@ /* eslint-disable import/no-extraneous-dependencies */ -import tar from 'tar' +import { x } from 'tar' import { Readable } from 'stream' import { pipeline } from 'stream/promises' @@ -103,7 +103,7 @@ export async function downloadAndExtractRepo( await downloadTarStream( `https://codeload.github.com/${username}/${name}/tar.gz/${branch}` ), - tar.x({ + x({ cwd: root, strip: filePath ? filePath.split('/').length + 1 : 1, filter: (p) => @@ -125,7 +125,7 @@ export async function downloadAndExtractExample(root: string, name: string) { await downloadTarStream( 'https://codeload.github.com/vercel/next.js/tar.gz/canary' ), - tar.x({ + x({ cwd: root, strip: 2 + name.split('/').length, filter: (p) => p.includes(`next.js-canary/examples/${name}/`), diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index aeb46adb4ba53c..b7bd7580275095 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "keywords": [ "react", "next", @@ -34,7 +34,7 @@ "@types/cross-spawn": "6.0.0", "@types/node": "^20.12.3", "@types/prompts": "2.4.2", - "@types/tar": "6.1.5", + "@types/tar": "6.1.13", "@types/validate-npm-package-name": "4.0.2", "@vercel/ncc": "0.34.0", "async-retry": "1.3.1", @@ -46,7 +46,7 @@ "picocolors": "1.0.0", "prettier-plugin-tailwindcss": "0.3.0", "prompts": "2.4.2", - "tar": "6.1.15", + "tar": "7.2.0", "update-check": "1.5.4", "validate-npm-package-name": "5.0.1" }, diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index cdd71ecb24d82b..9c749cb721812b 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "description": "ESLint configuration used by Next.js.", "main": "index.js", "license": "MIT", @@ -10,7 +10,7 @@ }, "homepage": "https://nextjs.org/docs/app/building-your-application/configuring/eslint#eslint-config", "dependencies": { - "@next/eslint-plugin-next": "15.0.0-canary.17", + "@next/eslint-plugin-next": "15.0.0-canary.22", "@rushstack/eslint-patch": "^1.3.3", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", "eslint-import-resolver-node": "^0.3.6", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index d8e71eb67747bf..6fba251782c692 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -1,6 +1,6 @@ { "name": "@next/eslint-plugin-next", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "description": "ESLint plugin for Next.js.", "main": "dist/index.js", "license": "MIT", diff --git a/packages/font/package.json b/packages/font/package.json index 3f8f6ffea35419..96fba7c3a1a257 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,6 +1,6 @@ { "name": "@next/font", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "repository": { "url": "vercel/next.js", "directory": "packages/font" diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index 796a3a8cb1fa23..b1222791ec8aeb 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index a3fbeb5423ce02..c6103c8378600b 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 8d21c3be688d5d..15a7a147d7a90e 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index feb4de8ef6813f..e11228fcc5e6d9 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 34ac73de4f71e6..5a681b41664b63 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index 02534d0ed0b120..61452ba1ba9ce3 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 1d8d7afaed9c52..7ce2f97ee75c94 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index 154317f6873995..e27ccbdf355ef4 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "private": true, "scripts": { "clean": "node ../../scripts/rm.mjs native", diff --git a/packages/next/package.json b/packages/next/package.json index b0253fe338c17b..c28d832d00ccc7 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -93,7 +93,7 @@ ] }, "dependencies": { - "@next/env": "15.0.0-canary.17", + "@next/env": "15.0.0-canary.22", "@swc/helpers": "0.5.11", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -157,10 +157,10 @@ "@jest/types": "29.5.0", "@mswjs/interceptors": "0.23.0", "@napi-rs/triples": "1.2.0", - "@next/polyfill-module": "15.0.0-canary.17", - "@next/polyfill-nomodule": "15.0.0-canary.17", - "@next/react-refresh-utils": "15.0.0-canary.17", - "@next/swc": "15.0.0-canary.17", + "@next/polyfill-module": "15.0.0-canary.22", + "@next/polyfill-nomodule": "15.0.0-canary.22", + "@next/react-refresh-utils": "15.0.0-canary.22", + "@next/swc": "15.0.0-canary.22", "@opentelemetry/api": "1.6.0", "@playwright/test": "1.41.2", "@swc/core": "1.5.7", @@ -206,7 +206,7 @@ "@types/ws": "8.2.0", "@vercel/ncc": "0.34.0", "@vercel/nft": "0.27.1", - "@vercel/turbopack-ecmascript-runtime": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-240607.1", + "@vercel/turbopack-ecmascript-runtime": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-240607.3", "acorn": "8.11.3", "amphtml-validator": "1.0.35", "anser": "1.4.9", diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 68d2c7f220255e..5e2d15e62d2aaf 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -159,16 +159,12 @@ import { startTypeChecking } from './type-check' import { generateInterceptionRoutesRewrites } from '../lib/generate-interception-routes-rewrites' import { buildDataRoute } from '../server/lib/router-utils/build-data-route' -import { initialize as initializeIncrementalCache } from '../server/lib/incremental-cache-server' -import { nodeFs } from '../server/lib/node-fs-methods' import { collectBuildTraces } from './collect-build-traces' import type { BuildTraceContext } from './webpack/plugins/next-trace-entrypoints-plugin' import { formatManifest } from './manifests/formatter/format-manifest' import { getStartServerInfo, logStartInfo } from '../server/lib/app-info-log' import type { NextEnabledDirectories } from '../server/base-server' import { hasCustomExportOutput } from '../export/utils' -import { interopDefault } from '../lib/interop-default' -import { formatDynamicImportPath } from '../lib/format-dynamic-import-path' import { isInterceptionRouteAppPath } from '../server/lib/interception-routes' import { getTurbopackJsConfig, @@ -578,11 +574,7 @@ type PageDataCollectionKeys = Exclude< 'exportPage' > -function createStaticWorker( - config: NextConfigComplete, - incrementalCacheIpcPort?: number, - incrementalCacheIpcValidationKey?: string -): StaticWorker { +function createStaticWorker(config: NextConfigComplete): StaticWorker { let infoPrinted = false const timeout = config.staticPageGenerationTimeout || 0 @@ -622,13 +614,7 @@ function createStaticWorker( }, numWorkers: getNumberOfWorkers(config), forkOptions: { - env: { - ...process.env, - __NEXT_INCREMENTAL_CACHE_IPC_PORT: incrementalCacheIpcPort - ? incrementalCacheIpcPort + '' - : undefined, - __NEXT_INCREMENTAL_CACHE_IPC_KEY: incrementalCacheIpcValidationKey, - }, + env: process.env, }, enableWorkerThreads: config.experimental.workerThreads, exposedMethods: staticWorkerExposedMethods, @@ -637,8 +623,6 @@ function createStaticWorker( async function writeFullyStaticExport( config: NextConfigComplete, - incrementalCacheIpcPort: number | undefined, - incrementalCacheIpcValidationKey: string | undefined, dir: string, enabledDirectories: NextEnabledDirectories, configOutDir: string, @@ -647,16 +631,8 @@ async function writeFullyStaticExport( const exportApp = require('../export') .default as typeof import('../export').default - const pagesWorker = createStaticWorker( - config, - incrementalCacheIpcPort, - incrementalCacheIpcValidationKey - ) - const appWorker = createStaticWorker( - config, - incrementalCacheIpcPort, - incrementalCacheIpcValidationKey - ) + const pagesWorker = createStaticWorker(config) + const appWorker = createStaticWorker(config) await exportApp( dir, @@ -1783,62 +1759,8 @@ export default async function build( process.env.NEXT_PHASE = PHASE_PRODUCTION_BUILD - let incrementalCacheIpcPort - let incrementalCacheIpcValidationKey - - if (config.experimental.staticWorkerRequestDeduping) { - let CacheHandler - if (cacheHandler) { - CacheHandler = interopDefault( - await import(formatDynamicImportPath(dir, cacheHandler)).then( - (mod) => mod.default || mod - ) - ) - } - - const cacheInitialization = await initializeIncrementalCache({ - fs: nodeFs, - dev: false, - pagesDir: true, - appDir: true, - fetchCache: true, - flushToDisk: ciEnvironment.hasNextSupport - ? false - : config.experimental.isrFlushToDisk, - serverDistDir: path.join(distDir, 'server'), - fetchCacheKeyPrefix: config.experimental.fetchCacheKeyPrefix, - maxMemoryCacheSize: config.cacheMaxMemorySize, - getPrerenderManifest: () => ({ - version: -1 as any, // letting us know this doesn't conform to spec - routes: {}, - dynamicRoutes: {}, - notFoundRoutes: [], - preview: null as any, // `preview` is special case read in next-dev-server - }), - requestHeaders: {}, - CurCacheHandler: CacheHandler, - minimalMode: ciEnvironment.hasNextSupport, - allowedRevalidateHeaderKeys: - config.experimental.allowedRevalidateHeaderKeys, - }) - - incrementalCacheIpcPort = cacheInitialization.ipcPort - incrementalCacheIpcValidationKey = cacheInitialization.ipcValidationKey - } - - const pagesStaticWorkers = createStaticWorker( - config, - - incrementalCacheIpcPort, - incrementalCacheIpcValidationKey - ) - const appStaticWorkers = appDir - ? createStaticWorker( - config, - incrementalCacheIpcPort, - incrementalCacheIpcValidationKey - ) - : undefined + const pagesStaticWorkers = createStaticWorker(config) + const appStaticWorkers = appDir ? createStaticWorker(config) : undefined const analysisBegin = process.hrtime() const staticCheckSpan = nextBuildSpan.traceChild('static-check') @@ -3392,8 +3314,6 @@ export default async function build( if (config.output === 'export') { await writeFullyStaticExport( config, - incrementalCacheIpcPort, - incrementalCacheIpcValidationKey, dir, enabledDirectories, configOutDir, diff --git a/packages/next/src/build/webpack/loaders/next-flight-loader/index.ts b/packages/next/src/build/webpack/loaders/next-flight-loader/index.ts index 79a0ae48ef6ea0..46d6b0ede81427 100644 --- a/packages/next/src/build/webpack/loaders/next-flight-loader/index.ts +++ b/packages/next/src/build/webpack/loaders/next-flight-loader/index.ts @@ -97,21 +97,24 @@ export default function transformSource( return } + // `proxy` is the module proxy that we treat the module as a client boundary. + // For ESM, we access the property of the module proxy directly for each export. + // This is bit hacky that treating using a CJS like module proxy for ESM's exports, + // but this will avoid creating nested proxies for each export. It will be improved in the future. let esmSource = `\ import { createProxy } from "${MODULE_PROXY_PATH}" + +const proxy = createProxy(String.raw\`${resourceKey}\`) ` let cnt = 0 for (const ref of clientRefs) { if (ref === '') { - esmSource += `\nexports[''] = createProxy(String.raw\`${resourceKey}#\`);` + esmSource += `exports[''] = proxy['']\n` } else if (ref === 'default') { - esmSource += `\ -export default createProxy(String.raw\`${resourceKey}#default\`); -` + esmSource += `export default createProxy(String.raw\`${resourceKey}#default\`);\n` } else { - esmSource += ` -const e${cnt} = createProxy(String.raw\`${resourceKey}#${ref}\`); -export { e${cnt++} as ${ref} };` + esmSource += `const e${cnt} = proxy["${ref}"];\n` + esmSource += `export { e${cnt++} as ${ref} };\n` } } diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts index fe2853eade6430..4ca7257d2e8210 100644 --- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts @@ -167,7 +167,7 @@ function navigateReducer_noPPR( updatedCanonicalUrl.split('#', 1)[0] let currentTree = state.tree - const currentCache = state.cache + let currentCache = state.cache let scrollableSegments: FlightSegmentPath[] = [] for (const flightDataPath of flightData) { const flightSegmentPath = flightDataPath.slice( @@ -258,6 +258,9 @@ function navigateReducer_noPPR( mutable.cache = cache } else if (applied) { mutable.cache = cache + // If we applied the cache, we update the "current cache" value so any other + // segments in the FlightDataPath will be able to reference the updated cache. + currentCache = cache } currentTree = newTree @@ -352,7 +355,7 @@ function navigateReducer_PPR( updatedCanonicalUrl.split('#', 1)[0] let currentTree = state.tree - const currentCache = state.cache + let currentCache = state.cache let scrollableSegments: FlightSegmentPath[] = [] // TODO: In practice, this is always a single item array. We probably // aren't going to every send multiple segments, at least not in this @@ -522,6 +525,9 @@ function navigateReducer_PPR( mutable.cache = cache } else if (applied) { mutable.cache = cache + // If we applied the cache, we update the "current cache" value so any other + // segments in the FlightDataPath will be able to reference the updated cache. + currentCache = cache } } diff --git a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts index 55c624b3959b6c..b7542244f090ee 100644 --- a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts @@ -125,6 +125,7 @@ export function refreshReducer( updatedTree: newTree, updatedCache: cache, includeNextUrl, + canonicalUrl: mutable.canonicalUrl || state.canonicalUrl, }) mutable.cache = cache diff --git a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts index 64027eb1aed085..2d580a4a0f6627 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts @@ -210,6 +210,11 @@ export function serverActionReducer( // Remove cache.data as it has been resolved at this point. mutable.inFlightServerAction = null + if (redirectLocation) { + const newHref = createHrefFromUrl(redirectLocation, false) + mutable.canonicalUrl = newHref + } + for (const flightDataPath of flightData) { // FlightDataPath with more than two items means unexpected Flight data was returned if (flightDataPath.length !== 4) { @@ -268,6 +273,7 @@ export function serverActionReducer( updatedTree: newTree, updatedCache: cache, includeNextUrl: Boolean(nextUrl), + canonicalUrl: mutable.canonicalUrl || state.canonicalUrl, }) mutable.cache = cache @@ -275,16 +281,9 @@ export function serverActionReducer( } mutable.patchedTree = newTree - mutable.canonicalUrl = href - currentTree = newTree } - if (redirectLocation) { - const newHref = createHrefFromUrl(redirectLocation, false) - mutable.canonicalUrl = newHref - } - resolve(actionResult) return handleMutable(state, mutable) diff --git a/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts b/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts index 05c1ddee9c3a27..99300a2b261312 100644 --- a/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts +++ b/packages/next/src/client/components/router-reducer/refetch-inactive-parallel-segments.ts @@ -10,6 +10,7 @@ interface RefreshInactiveParallelSegments { updatedTree: FlightRouterState updatedCache: CacheNode includeNextUrl: boolean + canonicalUrl: string } /** @@ -41,6 +42,7 @@ async function refreshInactiveParallelSegmentsImpl({ includeNextUrl, fetchedSegments, rootTree = updatedTree, + canonicalUrl, }: RefreshInactiveParallelSegments & { fetchedSegments: Set rootTree: FlightRouterState @@ -50,7 +52,7 @@ async function refreshInactiveParallelSegmentsImpl({ if ( refetchPath && - refetchPath !== location.pathname + location.search && + refetchPath !== canonicalUrl && refetchMarker === 'refresh' && // it's possible for the tree to contain multiple segments that contain data at the same URL // we keep track of them so we can dedupe the requests @@ -94,6 +96,7 @@ async function refreshInactiveParallelSegmentsImpl({ includeNextUrl, fetchedSegments, rootTree, + canonicalUrl, }) fetchPromises.push(parallelFetchPromise) diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 54462a090e24f4..1e56d4e2497c49 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -10,7 +10,7 @@ import type { } from './types' import type { StaticGenerationStore } from '../../client/components/static-generation-async-storage.external' import type { RequestStore } from '../../client/components/request-async-storage.external' -import { getRequestMeta, type NextParsedUrlQuery } from '../request-meta' +import type { NextParsedUrlQuery } from '../request-meta' import type { LoaderTree } from '../lib/app-dir-module' import type { AppPageModule } from '../route-modules/app-page/module' import type { ClientReferenceManifest } from '../../build/webpack/plugins/flight-manifest-plugin' @@ -37,8 +37,10 @@ import { import { canSegmentBeOverridden } from '../../client/components/match-segments' import { stripInternalQueries } from '../internal-utils' import { + NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE, NEXT_URL, + RSC_HEADER, } from '../../client/components/app-router-headers' import { createMetadataComponents, @@ -797,11 +799,13 @@ async function renderToHTMLOrFlightImpl( query = { ...query } stripInternalQueries(query) - const isRSCRequest = Boolean(getRequestMeta(req, 'isRSCRequest')) + // We read these values from the request object as, in certain cases, base-server + // will strip them to opt into different rendering behavior. + const isRSCRequest = req.headers[RSC_HEADER.toLowerCase()] !== undefined + const isPrefetchRSCRequest = + isRSCRequest && + req.headers[NEXT_ROUTER_PREFETCH_HEADER.toLowerCase()] !== undefined - const isPrefetchRSCRequest = Boolean( - getRequestMeta(req, 'isPrefetchRSCRequest') - ) /** * Router state provided from the client-side router. Used to handle rendering * from the common layout down. This value will be undefined if the request diff --git a/packages/next/src/server/config-schema.ts b/packages/next/src/server/config-schema.ts index 33dad9833d6b79..109add5c70c754 100644 --- a/packages/next/src/server/config-schema.ts +++ b/packages/next/src/server/config-schema.ts @@ -416,7 +416,6 @@ export const configSchema: zod.ZodType = z.lazy(() => .optional(), serverMinification: z.boolean().optional(), serverSourceMaps: z.boolean().optional(), - staticWorkerRequestDeduping: z.boolean().optional(), useWasmBinary: z.boolean().optional(), useLightningcss: z.boolean().optional(), useEarlyImport: z.boolean().optional(), diff --git a/packages/next/src/server/config-shared.ts b/packages/next/src/server/config-shared.ts index 5ced830ffced9b..da6248b3c5c0ae 100644 --- a/packages/next/src/server/config-shared.ts +++ b/packages/next/src/server/config-shared.ts @@ -438,11 +438,6 @@ export interface ExperimentalConfig { */ trustHostHeader?: boolean - /** - * Uses an IPC server to dedupe build-time requests to the cache handler - */ - staticWorkerRequestDeduping?: boolean - useWasmBinary?: boolean /** diff --git a/packages/next/src/server/lib/incremental-cache-server.ts b/packages/next/src/server/lib/incremental-cache-server.ts deleted file mode 100644 index 0f4a19f1801a75..00000000000000 --- a/packages/next/src/server/lib/incremental-cache-server.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { createIpcServer } from './server-ipc' -import { IncrementalCache } from './incremental-cache' - -let initializeResult: - | undefined - | { - ipcPort: number - ipcValidationKey: string - } - -export async function initialize( - ...constructorArgs: ConstructorParameters -): Promise> { - const incrementalCache = new IncrementalCache(...constructorArgs) - - const { ipcPort, ipcValidationKey } = await createIpcServer({ - async revalidateTag( - ...args: Parameters - ) { - return incrementalCache.revalidateTag(...args) - }, - - async get(...args: Parameters) { - return incrementalCache.get(...args) - }, - - async set(...args: Parameters) { - return incrementalCache.set(...args) - }, - - async lock(...args: Parameters) { - return incrementalCache.lock(...args) - }, - - async unlock(...args: Parameters) { - return incrementalCache.unlock(...args) - }, - } as any) - - return { - ipcPort, - ipcValidationKey, - } -} diff --git a/packages/next/src/server/lib/incremental-cache/index.ts b/packages/next/src/server/lib/incremental-cache/index.ts index 5eb87943946121..599a4a1830436d 100644 --- a/packages/next/src/server/lib/incremental-cache/index.ts +++ b/packages/next/src/server/lib/incremental-cache/index.ts @@ -234,31 +234,6 @@ export class IncrementalCache implements IncrementalCacheType { } async lock(cacheKey: string) { - if ( - process.env.__NEXT_INCREMENTAL_CACHE_IPC_PORT && - process.env.__NEXT_INCREMENTAL_CACHE_IPC_KEY && - process.env.NEXT_RUNTIME !== 'edge' - ) { - const invokeIpcMethod = require('../server-ipc/request-utils') - .invokeIpcMethod as typeof import('../server-ipc/request-utils').invokeIpcMethod - - await invokeIpcMethod({ - method: 'lock', - ipcPort: process.env.__NEXT_INCREMENTAL_CACHE_IPC_PORT, - ipcKey: process.env.__NEXT_INCREMENTAL_CACHE_IPC_KEY, - args: [cacheKey], - }) - - return async () => { - await invokeIpcMethod({ - method: 'unlock', - ipcPort: process.env.__NEXT_INCREMENTAL_CACHE_IPC_PORT, - ipcKey: process.env.__NEXT_INCREMENTAL_CACHE_IPC_KEY, - args: [cacheKey], - }) - } - } - let unlockNext: () => Promise = () => Promise.resolve() const existingLock = this.locks.get(cacheKey) @@ -279,21 +254,6 @@ export class IncrementalCache implements IncrementalCacheType { } async revalidateTag(tags: string | string[]): Promise { - if ( - process.env.__NEXT_INCREMENTAL_CACHE_IPC_PORT && - process.env.__NEXT_INCREMENTAL_CACHE_IPC_KEY && - process.env.NEXT_RUNTIME !== 'edge' - ) { - const invokeIpcMethod = require('../server-ipc/request-utils') - .invokeIpcMethod as typeof import('../server-ipc/request-utils').invokeIpcMethod - return invokeIpcMethod({ - method: 'revalidateTag', - ipcPort: process.env.__NEXT_INCREMENTAL_CACHE_IPC_PORT, - ipcKey: process.env.__NEXT_INCREMENTAL_CACHE_IPC_KEY, - args: [...arguments], - }) - } - return this.cacheHandler?.revalidateTag?.(tags) } @@ -433,22 +393,6 @@ export class IncrementalCache implements IncrementalCacheType { isRoutePPREnabled?: boolean } = {} ): Promise { - if ( - process.env.__NEXT_INCREMENTAL_CACHE_IPC_PORT && - process.env.__NEXT_INCREMENTAL_CACHE_IPC_KEY && - process.env.NEXT_RUNTIME !== 'edge' - ) { - const invokeIpcMethod = require('../server-ipc/request-utils') - .invokeIpcMethod as typeof import('../server-ipc/request-utils').invokeIpcMethod - - return invokeIpcMethod({ - method: 'get', - ipcPort: process.env.__NEXT_INCREMENTAL_CACHE_IPC_PORT, - ipcKey: process.env.__NEXT_INCREMENTAL_CACHE_IPC_KEY, - args: [...arguments], - }) - } - // we don't leverage the prerender cache in dev mode // so that getStaticProps is always called for easier debugging if ( @@ -556,22 +500,6 @@ export class IncrementalCache implements IncrementalCacheType { isRoutePPREnabled?: boolean } ) { - if ( - process.env.__NEXT_INCREMENTAL_CACHE_IPC_PORT && - process.env.__NEXT_INCREMENTAL_CACHE_IPC_KEY && - process.env.NEXT_RUNTIME !== 'edge' - ) { - const invokeIpcMethod = require('../server-ipc/request-utils') - .invokeIpcMethod as typeof import('../server-ipc/request-utils').invokeIpcMethod - - return invokeIpcMethod({ - method: 'set', - ipcPort: process.env.__NEXT_INCREMENTAL_CACHE_IPC_PORT, - ipcKey: process.env.__NEXT_INCREMENTAL_CACHE_IPC_KEY, - args: [...arguments], - }) - } - if (this.disableForTestmode || (this.dev && !ctx.fetchCache)) return // FetchCache has upper limit of 2MB per-entry currently const itemSize = JSON.stringify(data).length diff --git a/packages/next/src/server/lib/server-ipc/index.ts b/packages/next/src/server/lib/server-ipc/index.ts deleted file mode 100644 index 825331f95daa48..00000000000000 --- a/packages/next/src/server/lib/server-ipc/index.ts +++ /dev/null @@ -1,96 +0,0 @@ -import type NextServer from '../../next-server' -import { errorToJSON } from '../../render' -import crypto from 'crypto' -import isError from '../../../lib/is-error' -import { deserializeErr } from './request-utils' - -// we can't use process.send as jest-worker relies on -// it already and can cause unexpected message errors -// so we create an IPC server for communicating -export async function createIpcServer( - server: InstanceType -): Promise<{ - ipcPort: number - ipcServer: import('http').Server - ipcValidationKey: string -}> { - // Generate a random key in memory to validate messages from other processes. - // This is just a simple guard against other processes attempting to send - // traffic to the IPC server. - const ipcValidationKey = crypto.randomBytes(32).toString('hex') - - const ipcServer = (require('http') as typeof import('http')).createServer( - async (req, res) => { - try { - const url = new URL(req.url || '/', 'http://n') - const key = url.searchParams.get('key') - - if (key !== ipcValidationKey) { - return res.end() - } - - const method = url.searchParams.get('method') - let body = await new Promise((resolve, reject) => { - let str = '' - req.on('data', (chunk) => { - str += chunk - }) - req.on('end', () => { - resolve(str) - }) - req.on('error', (err) => { - reject(err) - }) - res.on('close', function () { - let aborted = !res.writableFinished - if (aborted) { - reject(new Error('ipc request aborted')) - } - }) - }) - const args: any[] = JSON.parse(body || '[]') - - if (!method || !Array.isArray(args)) { - return res.end() - } - - if (typeof (server as any)[method] === 'function') { - if (method === 'logErrorWithOriginalStack' && args[0]?.stack) { - args[0] = deserializeErr(args[0]) - } - let result = await (server as any)[method](...args) - - if (result && typeof result === 'object' && result.stack) { - result = errorToJSON(result) - } - res.end(JSON.stringify(result || '')) - } - } catch (err: any) { - if (isError(err) && err.code !== 'ENOENT') { - console.error(err) - } - res.end( - JSON.stringify({ - err: { name: err.name, message: err.message, stack: err.stack }, - }) - ) - } - } - ) - - const ipcPort = await new Promise((resolveIpc) => { - ipcServer.listen(0, server.hostname, () => { - const addr = ipcServer.address() - - if (addr && typeof addr === 'object') { - resolveIpc(addr.port) - } - }) - }) - - return { - ipcPort, - ipcServer, - ipcValidationKey, - } -} diff --git a/packages/next/src/server/lib/server-ipc/invoke-request.ts b/packages/next/src/server/lib/server-ipc/invoke-request.ts deleted file mode 100644 index 7fd44d96dd9ccf..00000000000000 --- a/packages/next/src/server/lib/server-ipc/invoke-request.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { IncomingMessage } from 'http' -import type { Readable } from 'stream' -import { filterReqHeaders, ipcForbiddenHeaders } from './utils' - -export const invokeRequest = async ( - targetUrl: string, - requestInit: { - headers: IncomingMessage['headers'] - method: IncomingMessage['method'] - signal?: AbortSignal - }, - readableBody?: string | Readable | ReadableStream -) => { - const invokeHeaders = filterReqHeaders( - { - 'cache-control': '', - ...requestInit.headers, - }, - ipcForbiddenHeaders - ) as IncomingMessage['headers'] - - return await fetch(targetUrl, { - headers: invokeHeaders as any as Headers, - method: requestInit.method, - redirect: 'manual', - signal: requestInit.signal, - - ...(requestInit.method !== 'GET' && - requestInit.method !== 'HEAD' && - readableBody - ? { - body: readableBody as BodyInit, - duplex: 'half', - } - : {}), - - next: { - // @ts-ignore - internal: true, - }, - }) -} diff --git a/packages/next/src/server/lib/server-ipc/request-utils.ts b/packages/next/src/server/lib/server-ipc/request-utils.ts deleted file mode 100644 index baff1072308a97..00000000000000 --- a/packages/next/src/server/lib/server-ipc/request-utils.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { decorateServerError } from '../../../shared/lib/error-source' -import { PageNotFoundError } from '../../../shared/lib/utils' -import { invokeRequest } from './invoke-request' - -export const deserializeErr = (serializedErr: any) => { - if ( - !serializedErr || - typeof serializedErr !== 'object' || - !serializedErr.stack - ) { - return serializedErr - } - let ErrorType: any = Error - - if (serializedErr.name === 'PageNotFoundError') { - ErrorType = PageNotFoundError - } - - const err = new ErrorType(serializedErr.message) - err.stack = serializedErr.stack - err.name = serializedErr.name - ;(err as any).digest = serializedErr.digest - - if ( - process.env.NODE_ENV === 'development' && - process.env.NEXT_RUNTIME !== 'edge' - ) { - decorateServerError(err, serializedErr.source || 'server') - } - return err -} - -export async function invokeIpcMethod({ - fetchHostname = 'localhost', - method, - args, - ipcPort, - ipcKey, -}: { - fetchHostname?: string - method: string - args: any[] - ipcPort?: string - ipcKey?: string -}): Promise { - if (ipcPort) { - const res = await invokeRequest( - `http://${fetchHostname}:${ipcPort}?key=${ipcKey}&method=${ - method as string - }`, - { - method: 'POST', - headers: {}, - }, - JSON.stringify(args) - ) - const body = await res.text() - - if (body.startsWith('{') && body.endsWith('}')) { - const parsedBody = JSON.parse(body) - - if ( - parsedBody && - typeof parsedBody === 'object' && - 'err' in parsedBody && - 'stack' in parsedBody.err - ) { - throw deserializeErr(parsedBody.err) - } - return parsedBody - } - } -} diff --git a/packages/next/src/server/web/spec-extension/unstable-cache.ts b/packages/next/src/server/web/spec-extension/unstable-cache.ts index e700985c714532..56e0801492f182 100644 --- a/packages/next/src/server/web/spec-extension/unstable-cache.ts +++ b/packages/next/src/server/web/spec-extension/unstable-cache.ts @@ -105,12 +105,24 @@ export function unstable_cache( } const incrementalCache = maybeIncrementalCache + const { pathname, searchParams } = new URL( + store?.urlPathname || '/', + 'http://n' + ) + const sortedSearchKeys = [...searchParams.keys()].sort((a, b) => { + return a.localeCompare(b) + }) + const sortedSearch = sortedSearchKeys + .map((key) => searchParams.get(key)) + .join('&') + // Construct the complete cache key for this function invocation // @TODO stringify is likely not safe here. We will coerce undefined to null which will make // the keyspace smaller than the execution space const invocationKey = `${fixedKey}-${JSON.stringify(args)}` const cacheKey = await incrementalCache.fetchCacheKey(invocationKey) - const fetchUrl = `unstable_cache ${cb.name ? ` ${cb.name}` : cacheKey}` + // $urlWithPath,$sortedQueryStringKeys,$hashOfEveryThingElse + const fetchUrl = `unstable_cache ${pathname}${sortedSearch.length ? '?' : ''}${sortedSearch} ${cb.name ? ` ${cb.name}` : cacheKey}` const fetchIdx = (store ? store.nextFetchId : noStoreFetchIdx) ?? 1 if (store) { diff --git a/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx b/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx index 31b6e459e93d8a..5950b1d82ce2fa 100644 --- a/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx +++ b/packages/next/src/shared/lib/lazy-dynamic/loadable.tsx @@ -15,7 +15,7 @@ function convertModule

( // Cases: // mod: { default: Component } // mod: Component - // mod: { $$typeof, default: proxy(Component) } + // mod: { default: proxy(Component) } // mod: proxy(Component) const hasDefault = mod && 'default' in mod return { diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index 503eb636d4cd6f..dd6110978113b7 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-refresh-utils", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", diff --git a/packages/third-parties/package.json b/packages/third-parties/package.json index 0038bc859de440..19a834ac99d9ec 100644 --- a/packages/third-parties/package.json +++ b/packages/third-parties/package.json @@ -1,6 +1,6 @@ { "name": "@next/third-parties", - "version": "15.0.0-canary.17", + "version": "15.0.0-canary.22", "repository": { "url": "vercel/next.js", "directory": "packages/third-parties" @@ -26,7 +26,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "15.0.0-canary.17", + "next": "15.0.0-canary.22", "outdent": "0.8.0", "prettier": "2.5.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd2b943f7ce034..31c454c3fbd2f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -705,8 +705,8 @@ importers: specifier: 2.4.2 version: 2.4.2 '@types/tar': - specifier: 6.1.5 - version: 6.1.5 + specifier: 6.1.13 + version: 6.1.13 '@types/validate-npm-package-name': specifier: 4.0.2 version: 4.0.2 @@ -741,8 +741,8 @@ importers: specifier: 2.4.2 version: 2.4.2 tar: - specifier: 6.1.15 - version: 6.1.15 + specifier: 7.2.0 + version: 7.2.0 update-check: specifier: 1.5.4 version: 1.5.4 @@ -753,7 +753,7 @@ importers: packages/eslint-config-next: dependencies: '@next/eslint-plugin-next': - specifier: 15.0.0-canary.17 + specifier: 15.0.0-canary.22 version: link:../eslint-plugin-next '@rushstack/eslint-patch': specifier: ^1.3.3 @@ -815,7 +815,7 @@ importers: packages/next: dependencies: '@next/env': - specifier: 15.0.0-canary.17 + specifier: 15.0.0-canary.22 version: link:../next-env '@swc/helpers': specifier: 0.5.11 @@ -943,16 +943,16 @@ importers: specifier: 1.2.0 version: 1.2.0 '@next/polyfill-module': - specifier: 15.0.0-canary.17 + specifier: 15.0.0-canary.22 version: link:../next-polyfill-module '@next/polyfill-nomodule': - specifier: 15.0.0-canary.17 + specifier: 15.0.0-canary.22 version: link:../next-polyfill-nomodule '@next/react-refresh-utils': - specifier: 15.0.0-canary.17 + specifier: 15.0.0-canary.22 version: link:../react-refresh-utils '@next/swc': - specifier: 15.0.0-canary.17 + specifier: 15.0.0-canary.22 version: link:../next-swc '@opentelemetry/api': specifier: 1.6.0 @@ -1090,8 +1090,8 @@ importers: specifier: 0.27.1 version: 0.27.1 '@vercel/turbopack-ecmascript-runtime': - specifier: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-240607.1 - version: '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-240607.1' + specifier: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-240607.3 + version: '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-240607.3' acorn: specifier: 8.11.3 version: 8.11.3 @@ -1576,7 +1576,7 @@ importers: version: 1.0.20 devDependencies: next: - specifier: 15.0.0-canary.17 + specifier: 15.0.0-canary.22 version: link:../next outdent: specifier: 0.8.0 @@ -4393,7 +4393,13 @@ packages: strip-ansi-cjs: /strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: false + + /@isaacs/fs-minipass@4.0.1: + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + dependencies: + minipass: 7.1.2 + dev: true /@istanbuljs/load-nyc-config@1.0.0: resolution: {integrity: sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==} @@ -6234,7 +6240,6 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} requiresBuild: true - dev: false optional: true /@pkgr/utils@2.3.1: @@ -7476,6 +7481,13 @@ packages: resolution: {integrity: sha512-ijt3zdHi2DmZxQpQTmozXszzDo78V4R3EdvX0jFMfnMH2ZzQSmCbaWOMPGXFUYSzSIdStv78HDjg32m5dxc+tA==} dev: true + /@types/tar@6.1.13: + resolution: {integrity: sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw==} + dependencies: + '@types/node': 20.12.3 + minipass: 4.2.8 + dev: true + /@types/tar@6.1.5: resolution: {integrity: sha512-qm2I/RlZij5RofuY7vohTpYNaYcrSQlN2MyjucQc7ZweDwaEWkdN/EeNh6e9zjK6uEm6PwjdMXkcj05BxZdX1Q==} dependencies: @@ -9625,6 +9637,11 @@ packages: engines: {node: '>=10'} dev: true + /chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + dev: true + /chrome-trace-event@1.0.2: resolution: {integrity: sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==} engines: {node: '>=6.0'} @@ -11490,7 +11507,6 @@ packages: /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: false /ecc-jsbn@0.1.2: resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} @@ -11574,7 +11590,6 @@ packages: /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: false /emojis-list@3.0.0: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} @@ -13142,7 +13157,6 @@ packages: dependencies: cross-spawn: 7.0.3 signal-exit: 4.0.2 - dev: false /forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} @@ -13612,7 +13626,6 @@ packages: minimatch: 9.0.3 minipass: 5.0.0 path-scurry: 1.10.1 - dev: false /glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} @@ -15413,7 +15426,6 @@ packages: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: false /jake@10.8.5: resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==} @@ -17958,7 +17970,6 @@ packages: engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 - dev: false /minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} @@ -18040,6 +18051,11 @@ packages: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} + /minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true + /minizlib@1.3.3: resolution: {integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==} dependencies: @@ -18054,6 +18070,14 @@ packages: yallist: 4.0.0 dev: true + /minizlib@3.0.1: + resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==} + engines: {node: '>= 18'} + dependencies: + minipass: 7.1.2 + rimraf: 5.0.7 + dev: true + /mixin-deep@1.3.2: resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} engines: {node: '>=0.10.0'} @@ -18089,6 +18113,12 @@ packages: hasBin: true dev: true + /mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + dev: true + /modify-values@1.0.1: resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} engines: {node: '>=0.10.0'} @@ -19463,7 +19493,6 @@ packages: dependencies: lru-cache: 10.0.0 minipass: 5.0.0 - dev: false /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} @@ -22243,6 +22272,14 @@ packages: dependencies: glob: 7.1.7 + /rimraf@5.0.7: + resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==} + engines: {node: '>=14.18'} + hasBin: true + dependencies: + glob: 10.3.10 + dev: true + /ripemd160@2.0.2: resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} dependencies: @@ -23269,7 +23306,6 @@ packages: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: false /string-width@7.1.0: resolution: {integrity: sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==} @@ -23811,6 +23847,18 @@ packages: yallist: 4.0.0 dev: true + /tar@7.2.0: + resolution: {integrity: sha512-hctwP0Nb4AB60bj8WQgRYaMOuJYRAPMGiQUAotms5igN8ppfQM+IvjQ5HcKu1MaZh2Wy2KWVTe563Yj8dfc14w==} + engines: {node: '>=18'} + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.1 + mkdirp: 3.0.1 + yallist: 5.0.0 + dev: true + /taskr@1.1.0: resolution: {integrity: sha1-TynQrOJvTerppHjqv5qgQy6IRDg=} engines: {node: '>= 4.6'} @@ -25527,7 +25575,6 @@ packages: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: false /wrap-ansi@9.0.0: resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} @@ -25705,6 +25752,11 @@ packages: /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + /yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + dev: true + /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -25838,8 +25890,8 @@ 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-240607.1': - resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-240607.1} + '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-240607.3': + resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-240607.3} name: '@vercel/turbopack-ecmascript-runtime' version: 0.0.0 dependencies: diff --git a/test/development/acceptance-app/dynamic-metadata-error.test.ts b/test/development/acceptance-app/dynamic-metadata-error.test.ts new file mode 100644 index 00000000000000..4c478d31d442be --- /dev/null +++ b/test/development/acceptance-app/dynamic-metadata-error.test.ts @@ -0,0 +1,104 @@ +/* eslint-env jest */ +import path from 'path' +import { sandbox } from 'development-sandbox' +import { FileRef, nextTestSetup } from 'e2e-utils' +import { retry } from 'next-test-utils' + +describe('dynamic metadata error', () => { + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), + skipStart: true, + }) + + it('should error when id is missing in generateImageMetadata', async () => { + const iconFilePath = 'app/metadata-base/unset/icon.tsx' + const contentMissingIdProperty = ` + import { ImageResponse } from 'next/og' + export async function generateImageMetadata() { + return [ + { + contentType: 'image/png', + size: { width: 48, height: 48 }, + // id: 100, + }, + { + contentType: 'image/png', + size: { width: 48, height: 48 }, + id: 101, + }, + ] + } + + export default function icon() { + return new ImageResponse(

icon
) + } + ` + const { cleanup } = await sandbox( + next, + new Map([[iconFilePath, contentMissingIdProperty]]), + '/metadata-base/unset/icon/100' + ) + + await retry(async () => { + expect(next.cliOutput).toContain( + `id property is required for every item returned from generateImageMetadata` + ) + }) + + await cleanup() + }) + + it('should error when id is missing in generateSitemaps', async () => { + const sitemapFilePath = 'app/metadata-base/unset/sitemap.tsx' + const contentMissingIdProperty = ` + import { MetadataRoute } from 'next' + + export async function generateSitemaps() { + return [ + { }, + ] + } + + export default function sitemap({ id }): MetadataRoute.Sitemap { + return [ + { + url: 'https://example.com/', + lastModified: '2021-01-01', + }, + ] + }` + + const { cleanup } = await sandbox( + next, + new Map([[sitemapFilePath, contentMissingIdProperty]]), + '/metadata-base/unset/sitemap/100' + ) + + await retry(async () => { + expect(next.cliOutput).toContain( + `id property is required for every item returned from generateSitemaps` + ) + }) + + await cleanup() + }) + + it('should error if the default export of dynamic image is missing', async () => { + const ogImageFilePath = 'app/opengraph-image.tsx' + const ogImageFileContentWithoutDefaultExport = ` + // Missing default export + export function foo() {} + ` + + const { cleanup } = await sandbox( + next, + new Map([[ogImageFilePath, ogImageFileContentWithoutDefaultExport]]), + '/opengraph-image' + ) + await retry(async () => { + expect(next.cliOutput).toContain(`Default export is missing in`) + }) + + await cleanup() + }) +}) diff --git a/test/e2e/app-dir/app-client-cache/client-cache.defaults.test.ts b/test/e2e/app-dir/app-client-cache/client-cache.defaults.test.ts index f8a168aca14c74..8801cc50b63aba 100644 --- a/test/e2e/app-dir/app-client-cache/client-cache.defaults.test.ts +++ b/test/e2e/app-dir/app-client-cache/client-cache.defaults.test.ts @@ -7,10 +7,11 @@ import { fastForwardTo, getPathname, } from './test-utils' +import path from 'path' describe('app dir client cache semantics (default semantics)', () => { const { next, isNextDev } = nextTestSetup({ - files: __dirname, + files: path.join(__dirname, 'fixtures', 'regular'), }) if (isNextDev) { diff --git a/test/e2e/app-dir/app-client-cache/client-cache.experimental.test.ts b/test/e2e/app-dir/app-client-cache/client-cache.experimental.test.ts index 604ead35788625..b865df7202ec64 100644 --- a/test/e2e/app-dir/app-client-cache/client-cache.experimental.test.ts +++ b/test/e2e/app-dir/app-client-cache/client-cache.experimental.test.ts @@ -1,11 +1,12 @@ import { nextTestSetup } from 'e2e-utils' import { browserConfigWithFixedTime, fastForwardTo } from './test-utils' import { findAllTelemetryEvents } from 'next-test-utils' +import path from 'path' describe('app dir client cache semantics (experimental staleTimes)', () => { describe('dynamic: 0', () => { const { next, isNextDev } = nextTestSetup({ - files: __dirname, + files: path.join(__dirname, 'fixtures', 'regular'), nextConfig: { experimental: { staleTimes: { dynamic: 0 } }, }, @@ -242,7 +243,7 @@ describe('app dir client cache semantics (experimental staleTimes)', () => { describe('static: 180', () => { const { next, isNextDev } = nextTestSetup({ - files: __dirname, + files: path.join(__dirname, 'fixtures', 'regular'), nextConfig: { experimental: { staleTimes: { static: 180 } }, }, @@ -360,7 +361,7 @@ describe('app dir client cache semantics (experimental staleTimes)', () => { describe('dynamic: 0, static: 0', () => { const { next } = nextTestSetup({ - files: __dirname, + files: path.join(__dirname, 'fixtures', 'regular'), nextConfig: { experimental: { staleTimes: { dynamic: 0, static: 0 } }, }, diff --git a/test/e2e/app-dir/app-client-cache/client-cache.original.test.ts b/test/e2e/app-dir/app-client-cache/client-cache.original.test.ts index f5065702d6466f..8740e0fa75918e 100644 --- a/test/e2e/app-dir/app-client-cache/client-cache.original.test.ts +++ b/test/e2e/app-dir/app-client-cache/client-cache.original.test.ts @@ -7,11 +7,12 @@ import { fastForwardTo, getPathname, } from './test-utils' +import path from 'path' // This preserves existing tests for the 30s/5min heuristic (previous router defaults) describe('app dir client cache semantics (30s/5min)', () => { const { next, isNextDev } = nextTestSetup({ - files: __dirname, + files: path.join(__dirname, 'fixtures', 'regular'), nextConfig: { experimental: { staleTimes: { dynamic: 30, static: 180 } }, }, diff --git a/test/e2e/app-dir/app-client-cache/client-cache.parallel-routes.test.ts b/test/e2e/app-dir/app-client-cache/client-cache.parallel-routes.test.ts new file mode 100644 index 00000000000000..667963a60ce356 --- /dev/null +++ b/test/e2e/app-dir/app-client-cache/client-cache.parallel-routes.test.ts @@ -0,0 +1,87 @@ +import { nextTestSetup } from 'e2e-utils' +import { check } from 'next-test-utils' +import { BrowserInterface } from 'next-webdriver' +import { + browserConfigWithFixedTime, + createRequestsListener, + fastForwardTo, + getPathname, +} from './test-utils' +import path from 'path' + +describe('app dir client cache with parallel routes', () => { + const { next, isNextDev } = nextTestSetup({ + files: path.join(__dirname, 'fixtures', 'parallel-routes'), + }) + + if (isNextDev) { + // dev doesn't support prefetch={true} + it('should skip dev', () => {}) + return + } + + describe('prefetch={true}', () => { + let browser: BrowserInterface + + beforeEach(async () => { + browser = (await next.browser( + '/', + browserConfigWithFixedTime + )) as BrowserInterface + }) + + it('should prefetch the full page', async () => { + const { getRequests, clearRequests } = + await createRequestsListener(browser) + await check(() => { + return getRequests().some( + ([url, didPartialPrefetch]) => + getPathname(url) === '/0' && !didPartialPrefetch + ) + ? 'success' + : 'fail' + }, 'success') + + clearRequests() + + await browser + .elementByCss('[href="/0"]') + .click() + .waitForElementByCss('#random-number') + + expect(getRequests().every(([url]) => getPathname(url) !== '/0')).toEqual( + true + ) + }) + + it('should re-use the cache for the full page, only for 5 mins', async () => { + const randomNumber = await browser + .elementByCss('[href="/0"]') + .click() + .waitForElementByCss('#random-number') + .text() + + await browser.elementByCss('[href="/"]').click() + + const number = await browser + .elementByCss('[href="/0"]') + .click() + .waitForElementByCss('#random-number') + .text() + + expect(number).toBe(randomNumber) + + await browser.eval(fastForwardTo, 5 * 60 * 1000) + + await browser.elementByCss('[href="/"]').click() + + const newNumber = await browser + .elementByCss('[href="/0"]') + .click() + .waitForElementByCss('#random-number') + .text() + + expect(newNumber).not.toBe(randomNumber) + }) + }) +}) diff --git a/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/@breadcrumbs/[id]/page.js b/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/@breadcrumbs/[id]/page.js new file mode 100644 index 00000000000000..43ade54c21804c --- /dev/null +++ b/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/@breadcrumbs/[id]/page.js @@ -0,0 +1,7 @@ +export default function Page({ params }) { + return ( +
+ Catchall
{JSON.stringify(params)}
{' '} +
+ ) +} diff --git a/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/@breadcrumbs/page.js b/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/@breadcrumbs/page.js new file mode 100644 index 00000000000000..9e02fe9730f1d0 --- /dev/null +++ b/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/@breadcrumbs/page.js @@ -0,0 +1,3 @@ +export default function Page() { + return
Root Breadcrumb
+} diff --git a/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/[id]/page.js b/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/[id]/page.js new file mode 100644 index 00000000000000..cd2e35b1f72a6d --- /dev/null +++ b/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/[id]/page.js @@ -0,0 +1,18 @@ +import Link from 'next/link' + +export default async function Page() { + const randomNumber = await new Promise((resolve) => { + setTimeout(() => { + resolve(Math.random()) + }, 1000) + }) + + return ( + <> +
+ Back to Home +
+
{randomNumber}
+ + ) +} diff --git a/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/layout.js b/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/layout.js new file mode 100644 index 00000000000000..2fc53540d0283a --- /dev/null +++ b/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/layout.js @@ -0,0 +1,12 @@ +export default function Root({ children, breadcrumbs }) { + return ( + + + +
{breadcrumbs}
+
Root Layout
+
{children}
+ + + ) +} diff --git a/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/page.js b/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/page.js new file mode 100644 index 00000000000000..9adcd6a6d0a698 --- /dev/null +++ b/test/e2e/app-dir/app-client-cache/fixtures/parallel-routes/app/page.js @@ -0,0 +1,11 @@ +import Link from 'next/link' + +export default function Page() { + return ( +
+ + To Dynamic Page + +
+ ) +} diff --git a/test/e2e/app-dir/app-client-cache/app/[id]/loading.js b/test/e2e/app-dir/app-client-cache/fixtures/regular/app/[id]/loading.js similarity index 100% rename from test/e2e/app-dir/app-client-cache/app/[id]/loading.js rename to test/e2e/app-dir/app-client-cache/fixtures/regular/app/[id]/loading.js diff --git a/test/e2e/app-dir/app-client-cache/app/[id]/page.js b/test/e2e/app-dir/app-client-cache/fixtures/regular/app/[id]/page.js similarity index 100% rename from test/e2e/app-dir/app-client-cache/app/[id]/page.js rename to test/e2e/app-dir/app-client-cache/fixtures/regular/app/[id]/page.js diff --git a/test/e2e/app-dir/app-client-cache/app/layout.js b/test/e2e/app-dir/app-client-cache/fixtures/regular/app/layout.js similarity index 100% rename from test/e2e/app-dir/app-client-cache/app/layout.js rename to test/e2e/app-dir/app-client-cache/fixtures/regular/app/layout.js diff --git a/test/e2e/app-dir/app-client-cache/app/null-loading/loading.js b/test/e2e/app-dir/app-client-cache/fixtures/regular/app/null-loading/loading.js similarity index 100% rename from test/e2e/app-dir/app-client-cache/app/null-loading/loading.js rename to test/e2e/app-dir/app-client-cache/fixtures/regular/app/null-loading/loading.js diff --git a/test/e2e/app-dir/app-client-cache/app/null-loading/page.js b/test/e2e/app-dir/app-client-cache/fixtures/regular/app/null-loading/page.js similarity index 100% rename from test/e2e/app-dir/app-client-cache/app/null-loading/page.js rename to test/e2e/app-dir/app-client-cache/fixtures/regular/app/null-loading/page.js diff --git a/test/e2e/app-dir/app-client-cache/app/page.js b/test/e2e/app-dir/app-client-cache/fixtures/regular/app/page.js similarity index 100% rename from test/e2e/app-dir/app-client-cache/app/page.js rename to test/e2e/app-dir/app-client-cache/fixtures/regular/app/page.js diff --git a/test/e2e/app-dir/app-client-cache/app/without-loading/[id]/page.js b/test/e2e/app-dir/app-client-cache/fixtures/regular/app/without-loading/[id]/page.js similarity index 100% rename from test/e2e/app-dir/app-client-cache/app/without-loading/[id]/page.js rename to test/e2e/app-dir/app-client-cache/fixtures/regular/app/without-loading/[id]/page.js diff --git a/test/e2e/app-dir/app-client-cache/app/without-loading/page.js b/test/e2e/app-dir/app-client-cache/fixtures/regular/app/without-loading/page.js similarity index 100% rename from test/e2e/app-dir/app-client-cache/app/without-loading/page.js rename to test/e2e/app-dir/app-client-cache/fixtures/regular/app/without-loading/page.js diff --git a/test/e2e/app-dir/app-fetch-deduping/app-fetch-deduping.test.ts b/test/e2e/app-dir/app-fetch-deduping/app-fetch-deduping.test.ts index 23c54b12787d1e..311baf3144d73a 100644 --- a/test/e2e/app-dir/app-fetch-deduping/app-fetch-deduping.test.ts +++ b/test/e2e/app-dir/app-fetch-deduping/app-fetch-deduping.test.ts @@ -1,55 +1,9 @@ -import { findPort, waitFor } from 'next-test-utils' -import http from 'http' +import { waitFor } from 'next-test-utils' import { outdent } from 'outdent' -import { isNextDev, isNextStart, nextTestSetup } from 'e2e-utils' +import { isNextDev, nextTestSetup } from 'e2e-utils' describe('app-fetch-deduping', () => { - if (isNextStart) { - describe('during static generation', () => { - const { next } = nextTestSetup({ files: __dirname, skipStart: true }) - let externalServerPort: number - let externalServer: http.Server - let requests = [] - - beforeAll(async () => { - externalServerPort = await findPort() - externalServer = http.createServer((req, res) => { - requests.push(req.url) - res.end(`Request ${req.url} received at ${Date.now()}`) - }) - - await new Promise((resolve, reject) => { - externalServer.listen(externalServerPort, () => { - resolve() - }) - - externalServer.once('error', (err) => { - reject(err) - }) - }) - }) - - beforeEach(() => { - requests = [] - }) - - afterAll(() => externalServer.close()) - - it('dedupes requests amongst static workers when experimental.staticWorkerRequestDeduping is enabled', async () => { - await next.patchFileFast( - 'next.config.js', - `module.exports = { - env: { TEST_SERVER_PORT: "${externalServerPort}" }, - experimental: { - staticWorkerRequestDeduping: true - } - }` - ) - await next.build() - expect(requests.length).toBe(1) - }) - }) - } else if (isNextDev) { + if (isNextDev) { describe('during next dev', () => { const { next } = nextTestSetup({ files: __dirname }) function invocation(cliOutput: string): number { diff --git a/test/e2e/app-dir/app-routes-client-component/app-routes-client-component.test.ts b/test/e2e/app-dir/app-routes-client-component/app-routes-client-component.test.ts index a98b19ff6eb379..b0a5079ac0a642 100644 --- a/test/e2e/app-dir/app-routes-client-component/app-routes-client-component.test.ts +++ b/test/e2e/app-dir/app-routes-client-component/app-routes-client-component.test.ts @@ -1,16 +1,14 @@ -import { FileRef, nextTestSetup } from 'e2e-utils' -import path from 'path' +import { nextTestSetup } from 'e2e-utils' describe('referencing a client component in an app route', () => { const { next } = nextTestSetup({ - files: new FileRef(path.join(__dirname)), + files: __dirname, }) it('responds without error', async () => { expect(JSON.parse(await next.render('/runtime'))).toEqual({ - // Turbopack's proxy components are functions - clientComponent: process.env.TURBOPACK ? 'function' : 'object', - myModuleClientComponent: process.env.TURBOPACK ? 'function' : 'object', + clientComponent: 'function', + myModuleClientComponent: 'function', }) }) }) diff --git a/test/e2e/app-dir/dynamic/app/dynamic/named-export/page.js b/test/e2e/app-dir/dynamic/app/dynamic/named-export/page.js index b767273ab60776..3285b72c5f5596 100644 --- a/test/e2e/app-dir/dynamic/app/dynamic/named-export/page.js +++ b/test/e2e/app-dir/dynamic/app/dynamic/named-export/page.js @@ -2,7 +2,7 @@ import dynamic from 'next/dynamic' const Button = dynamic(() => import('./client').then((mod) => { - return mod.Button + return { default: mod.Button } }) ) diff --git a/test/e2e/app-dir/fallback-prefetch/app/[id]/loading.tsx b/test/e2e/app-dir/fallback-prefetch/app/[id]/loading.tsx new file mode 100644 index 00000000000000..19c014ca89faa6 --- /dev/null +++ b/test/e2e/app-dir/fallback-prefetch/app/[id]/loading.tsx @@ -0,0 +1,3 @@ +export default function IdLoadingPage() { + return
Loading page...
+} diff --git a/test/e2e/app-dir/fallback-prefetch/app/[id]/page.tsx b/test/e2e/app-dir/fallback-prefetch/app/[id]/page.tsx new file mode 100644 index 00000000000000..04faa8ab2b8d96 --- /dev/null +++ b/test/e2e/app-dir/fallback-prefetch/app/[id]/page.tsx @@ -0,0 +1,22 @@ +import Link from 'next/link' + +export const dynamic = 'force-static' + +export async function generateStaticParams() { + return [] +} + +export default async function IdPage({ + params: { id }, +}: { + params: { id: string } +}) { + await new Promise((resolve) => setTimeout(resolve, Math.random() * 5000)) + + return ( +
+

{id} page

+ Go Home +
+ ) +} diff --git a/test/e2e/app-dir/fallback-prefetch/app/layout.tsx b/test/e2e/app-dir/fallback-prefetch/app/layout.tsx new file mode 100644 index 00000000000000..888614deda3ba5 --- /dev/null +++ b/test/e2e/app-dir/fallback-prefetch/app/layout.tsx @@ -0,0 +1,8 @@ +import { ReactNode } from 'react' +export default function Root({ children }: { children: ReactNode }) { + return ( + + {children} + + ) +} diff --git a/test/e2e/app-dir/fallback-prefetch/app/page.tsx b/test/e2e/app-dir/fallback-prefetch/app/page.tsx new file mode 100644 index 00000000000000..d2f12c1bcf16e5 --- /dev/null +++ b/test/e2e/app-dir/fallback-prefetch/app/page.tsx @@ -0,0 +1,15 @@ +import Link from 'next/link' + +export const dynamic = 'force-dynamic' + +export default function HomePage() { + const randomId = crypto.randomUUID() + + return ( +
+ + Go to Random Page + +
+ ) +} diff --git a/test/e2e/app-dir/fallback-prefetch/fallback-prefetch.test.ts b/test/e2e/app-dir/fallback-prefetch/fallback-prefetch.test.ts new file mode 100644 index 00000000000000..ec10619bef1c45 --- /dev/null +++ b/test/e2e/app-dir/fallback-prefetch/fallback-prefetch.test.ts @@ -0,0 +1,29 @@ +import { nextTestSetup } from 'e2e-utils' + +describe('fallback-prefetch', () => { + const { next } = nextTestSetup({ + files: __dirname, + }) + + it('should prefetch the page without errors', async () => { + let hasNetworkError = false + const browser = await next.browser('/', { + beforePageLoad: (page) => { + page.on('response', (response) => { + if (!response.ok()) { + hasNetworkError = true + } + }) + }, + }) + + // set a flag on the window to ensure we don't perform an MPA navigation when navigating to the prefetched link + await browser.eval('window.beforeNav = 1') + await browser.elementById('link-to-random').click() + + await browser.waitForElementByCss('#random-page') + + expect(await browser.eval('window.beforeNav')).toBe(1) + expect(hasNetworkError).toBe(false) + }) +}) diff --git a/test/e2e/app-dir/fallback-prefetch/next.config.js b/test/e2e/app-dir/fallback-prefetch/next.config.js new file mode 100644 index 00000000000000..807126e4cf0bf5 --- /dev/null +++ b/test/e2e/app-dir/fallback-prefetch/next.config.js @@ -0,0 +1,6 @@ +/** + * @type {import('next').NextConfig} + */ +const nextConfig = {} + +module.exports = nextConfig diff --git a/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts b/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts index 3a2cb1c6c0fc68..9e66abed21d09d 100644 --- a/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts +++ b/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts @@ -438,114 +438,6 @@ describe('app dir - metadata dynamic routes', () => { expect(twitterImage).toMatch(/\/metadata-base\/unset\/twitter-image\.png/) }) - if (isNextDev) { - it('should error when id is missing in generateImageMetadata', async () => { - const iconFilePath = 'app/metadata-base/unset/icon.tsx' - const contentMissingIdProperty = ` - import { ImageResponse } from 'next/og' - export async function generateImageMetadata() { - return [ - { - contentType: 'image/png', - size: { width: 48, height: 48 }, - // id: 100, - }, - { - contentType: 'image/png', - size: { width: 48, height: 48 }, - id: 101, - }, - ] - } - - export default function icon() { - return new ImageResponse(
icon
) - } - ` - - const originalOutputIndex = next.cliOutput.length - - await next.patchFile(iconFilePath, contentMissingIdProperty) - await next.fetch('/metadata-base/unset/icon/100') - - try { - await check(async () => { - expect(next.cliOutput.substring(originalOutputIndex)).toContain( - `id property is required for every item returned from generateImageMetadata` - ) - return 'success' - }, /success/) - } finally { - await next.deleteFile(iconFilePath) - await next.fetch('/metadata-base/unset/icon/100') - } - }) - - it('should error when id is missing in generateSitemaps', async () => { - const sitemapFilePath = 'app/metadata-base/unset/sitemap.tsx' - const contentMissingIdProperty = ` - import { MetadataRoute } from 'next' - - export async function generateSitemaps() { - return [ - { }, - ] - } - - export default function sitemap({ id }): MetadataRoute.Sitemap { - return [ - { - url: 'https://example.com/', - lastModified: '2021-01-01', - }, - ] - }` - - const originalOutputIndex = next.cliOutput.length - - await next.patchFile(sitemapFilePath, contentMissingIdProperty) - await next.fetch('/metadata-base/unset/sitemap/0') - - try { - await check(async () => { - expect(next.cliOutput.substring(originalOutputIndex)).toContain( - `id property is required for every item returned from generateSitemaps` - ) - return 'success' - }, /success/) - } finally { - await next.deleteFile(sitemapFilePath) - await next.fetch('/metadata-base/unset/sitemap/0') - } - }) - - it('should error if the default export of dynamic image is missing', async () => { - const ogImageFilePath = 'app/opengraph-image.tsx' - const ogImageFileContent = await next.readFile(ogImageFilePath) - const ogImageFileContentWithoutDefaultExport = ogImageFileContent.replace( - 'export default function', - 'export function' - ) - - try { - await next.patchFile( - ogImageFilePath, - ogImageFileContentWithoutDefaultExport - ) - const currentNextCliOutputLength = next.cliOutput.length - - await check(async () => { - await next.fetch('/opengraph-image') - const output = next.cliOutput.slice(currentNextCliOutputLength) - expect(output).toContain(`Default export is missing in`) - return 'success' - }, /success/) - } finally { - await next.patchFile(ogImageFilePath, ogImageFileContent) - } - }) - } - if (isNextStart) { it('should support edge runtime of image routes', async () => { const middlewareManifest = JSON.parse( diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/page.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/page.tsx index 3ef8e789935721..3b35ec77533544 100644 --- a/test/e2e/app-dir/parallel-routes-revalidation/app/page.tsx +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/page.tsx @@ -8,7 +8,7 @@ export default async function Home() { ).then((res) => res.text()) return ( -
+
Open Revalidate Modal Open Refresh Modal Open Redirect Modal diff --git a/test/e2e/app-dir/parallel-routes-revalidation/app/redirect/page.tsx b/test/e2e/app-dir/parallel-routes-revalidation/app/redirect/page.tsx index f451ae9b3b5bca..133a96c1208d65 100644 --- a/test/e2e/app-dir/parallel-routes-revalidation/app/redirect/page.tsx +++ b/test/e2e/app-dir/parallel-routes-revalidation/app/redirect/page.tsx @@ -8,7 +8,9 @@ export default function Page() { redirect('/') }} > - + ) } diff --git a/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts b/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts index df33fee9ed9b4f..bb61d0a4a86c2b 100644 --- a/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts +++ b/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts @@ -2,7 +2,7 @@ import { nextTestSetup } from 'e2e-utils' import { check, retry } from 'next-test-utils' describe('parallel-routes-revalidation', () => { - const { next } = nextTestSetup({ + const { next, isNextStart } = nextTestSetup({ files: __dirname, }) @@ -413,5 +413,38 @@ describe('parallel-routes-revalidation', () => { ) }) }) + + it('should not trigger a refresh for the page that is being redirected to', async () => { + const rscRequests = [] + const prefetchRequests = [] + const browser = await next.browser('/redirect', { + beforePageLoad(page) { + page.on('request', async (req) => { + const headers = await req.allHeaders() + if (headers['rsc']) { + const pathname = new URL(req.url()).pathname + + if (headers['next-router-prefetch']) { + prefetchRequests.push(pathname) + } else { + rscRequests.push(pathname) + } + } + }) + }, + }) + + await browser.elementByCss('button').click() + await browser.waitForElementByCss('#root-page') + await browser.waitForIdleNetwork() + + await retry(async () => { + expect(rscRequests.length).toBe(0) + + if (isNextStart) { + expect(prefetchRequests.length).toBe(4) + } + }) + }) }) })