diff --git a/.github/workflows/update_fonts_data.yml b/.github/workflows/update_fonts_data.yml index c47e205bf75c9..f1ba72107db19 100644 --- a/.github/workflows/update_fonts_data.yml +++ b/.github/workflows/update_fonts_data.yml @@ -13,6 +13,7 @@ env: jobs: create-pull-request: runs-on: ubuntu-latest + if: github.repository_owner == 'vercel' steps: - name: Checkout uses: actions/checkout@v3 diff --git a/Cargo.lock b/Cargo.lock index 3f591ab5ce8b0..8b0626960f6dc 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-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "serde", "smallvec", @@ -3493,6 +3493,7 @@ dependencies = [ "turbo-tasks", "turbopack-binding", "url", + "urlencoding", ] [[package]] @@ -3527,7 +3528,7 @@ dependencies = [ [[package]] name = "node-file-trace" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "serde", @@ -7638,7 +7639,7 @@ dependencies = [ [[package]] name = "turbo-tasks" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "async-trait", @@ -7670,7 +7671,7 @@ dependencies = [ [[package]] name = "turbo-tasks-build" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "cargo-lock", @@ -7682,7 +7683,7 @@ dependencies = [ [[package]] name = "turbo-tasks-bytes" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "bytes", @@ -7697,7 +7698,7 @@ dependencies = [ [[package]] name = "turbo-tasks-env" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "dotenvs", @@ -7711,7 +7712,7 @@ dependencies = [ [[package]] name = "turbo-tasks-fetch" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "indexmap 1.9.3", @@ -7728,7 +7729,7 @@ dependencies = [ [[package]] name = "turbo-tasks-fs" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "auto-hash-map", @@ -7758,7 +7759,7 @@ dependencies = [ [[package]] name = "turbo-tasks-hash" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "base16", "hex", @@ -7770,7 +7771,7 @@ dependencies = [ [[package]] name = "turbo-tasks-macros" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -7784,7 +7785,7 @@ dependencies = [ [[package]] name = "turbo-tasks-macros-shared" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "proc-macro2", "quote", @@ -7794,7 +7795,7 @@ dependencies = [ [[package]] name = "turbo-tasks-malloc" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "mimalloc", ] @@ -7802,7 +7803,7 @@ dependencies = [ [[package]] name = "turbo-tasks-memory" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "auto-hash-map", @@ -7827,7 +7828,7 @@ dependencies = [ [[package]] name = "turbopack" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "async-recursion", @@ -7858,7 +7859,7 @@ dependencies = [ [[package]] name = "turbopack-binding" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "auto-hash-map", "mdxjs", @@ -7898,7 +7899,7 @@ dependencies = [ [[package]] name = "turbopack-build" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "indexmap 1.9.3", @@ -7920,7 +7921,7 @@ dependencies = [ [[package]] name = "turbopack-cli-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "clap 4.4.2", @@ -7944,7 +7945,7 @@ dependencies = [ [[package]] name = "turbopack-core" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "async-recursion", @@ -7974,7 +7975,7 @@ dependencies = [ [[package]] name = "turbopack-css" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "async-trait", @@ -7996,7 +7997,7 @@ dependencies = [ [[package]] name = "turbopack-dev" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "indexmap 1.9.3", @@ -8020,7 +8021,7 @@ dependencies = [ [[package]] name = "turbopack-dev-server" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "async-compression", @@ -8057,7 +8058,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "async-trait", @@ -8091,7 +8092,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript-hmr-protocol" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "serde", "serde_json", @@ -8102,7 +8103,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript-plugins" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "async-trait", @@ -8125,7 +8126,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript-runtime" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "indoc", @@ -8142,7 +8143,7 @@ dependencies = [ [[package]] name = "turbopack-env" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "indexmap 1.9.3", @@ -8158,7 +8159,7 @@ dependencies = [ [[package]] name = "turbopack-image" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "base64 0.21.4", @@ -8178,7 +8179,7 @@ dependencies = [ [[package]] name = "turbopack-json" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "serde", @@ -8193,7 +8194,7 @@ dependencies = [ [[package]] name = "turbopack-mdx" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "mdxjs", @@ -8208,7 +8209,7 @@ dependencies = [ [[package]] name = "turbopack-node" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "async-stream", @@ -8243,7 +8244,7 @@ dependencies = [ [[package]] name = "turbopack-static" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "serde", @@ -8259,7 +8260,7 @@ dependencies = [ [[package]] name = "turbopack-swc-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "swc_core", "turbo-tasks", @@ -8270,7 +8271,7 @@ dependencies = [ [[package]] name = "turbopack-wasm" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.3#2df370f8a18291864f1241e8b8b52d999dfaa80f" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-231020.4#07e8e48458b553930be29b7d4a0da89204a4e59a" dependencies = [ "anyhow", "indexmap 1.9.3", diff --git a/Cargo.toml b/Cargo.toml index f8e9b1e969537..6392f39a306eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,11 +40,11 @@ swc_core = { version = "0.86.1", features = [ testing = { version = "0.35.0" } # Turbo crates -turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-231020.3" } +turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-231020.4" } # [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-231020.3" } +turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-231020.4" } # [TODO]: need to refactor embed_directory! macro usage in next-core -turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-231020.3" } +turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-231020.4" } # General Deps diff --git a/lerna.json b/lerna.json index 69200f9f4241f..05b6e27aaa675 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "13.5.7-canary.12" + "version": "13.5.7-canary.13" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index c0b73ee07dcfd..d00bf24601b3b 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 57d1249361e2c..102869b43365f 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "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": "13.5.7-canary.12", + "@next/eslint-plugin-next": "13.5.7-canary.13", "@rushstack/eslint-patch": "^1.3.3", "@typescript-eslint/parser": "^5.4.2 || ^6.0.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 357c59c097a40..74f3bb2bd4b88 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": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "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 b259ac3e0c374..5a0e1fb9962da 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,6 +1,6 @@ { "name": "@next/font", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "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 bef7fe11c142d..121cf043059b2 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 989dab1c737f1..d8beabd4c8347 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 077a9526707de..13599ebc10e1c 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 3766ca1d78b07..0d7267c3e1ffd 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 9bf86717dfbe2..5bd538e29e37c 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "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 4826621472cc0..0123080f66884 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "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 9b0fce616917e..44d6694114c0e 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-swc/crates/core/src/server_actions.rs b/packages/next-swc/crates/core/src/server_actions.rs index b9657a6daccb2..6a1966f0dc3b8 100644 --- a/packages/next-swc/crates/core/src/server_actions.rs +++ b/packages/next-swc/crates/core/src/server_actions.rs @@ -283,16 +283,75 @@ impl ServerActions { // export const $ACTION_myAction = async () => {} let mut new_params: Vec = vec![]; + let mut new_body: BlockStmtOrExpr = *a.body.clone(); - for i in 0..ids_from_closure.len() { + if !ids_from_closure.is_empty() { + // First argument is the encrypted closure variables new_params.push(Pat::Ident( - Ident::new(format!("$$ACTION_ARG_{}", i).into(), DUMMY_SP).into(), + Ident::new("$$ACTION_CLOSURE_BOUND".into(), DUMMY_SP).into(), )); + + // Also prepend the decryption decl into the body. + // var [arg1, arg2, arg3] = await decryptActionBoundArgs(actionId, + // $$ACTION_CLOSURE_BOUND) + let mut pats = vec![]; + for i in 0..ids_from_closure.len() { + pats.push(Some(Pat::Ident( + Ident::new(format!("$$ACTION_ARG_{}", i).into(), DUMMY_SP).into(), + ))); + } + let decryption_decl = VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Var, + declare: false, + decls: vec![VarDeclarator { + span: DUMMY_SP, + name: Pat::Array(ArrayPat { + span: DUMMY_SP, + elems: pats, + optional: false, + type_ann: None, + }), + init: Some(Box::new(Expr::Await(AwaitExpr { + span: DUMMY_SP, + arg: Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: quote_ident!("decryptActionBoundArgs").as_callee(), + args: vec![ + generate_action_id(&self.file_name, &export_name).as_arg(), + quote_ident!("$$ACTION_CLOSURE_BOUND").as_arg(), + ], + type_args: None, + })), + }))), + definite: Default::default(), + }], + }; + + match &mut new_body { + BlockStmtOrExpr::BlockStmt(body) => { + body.stmts.insert(0, decryption_decl.into()); + } + BlockStmtOrExpr::Expr(body_expr) => { + new_body = BlockStmtOrExpr::BlockStmt(BlockStmt { + span: DUMMY_SP, + stmts: vec![ + decryption_decl.into(), + Stmt::Return(ReturnStmt { + span: DUMMY_SP, + arg: Some(body_expr.take()), + }), + ], + }); + } + } } + for p in a.params.iter() { new_params.push(p.clone()); } + // Create the action export decl self.extra_items .push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { span: DUMMY_SP, @@ -302,9 +361,10 @@ impl ServerActions { declare: Default::default(), decls: vec![VarDeclarator { span: DUMMY_SP, - name: action_ident.into(), + name: action_ident.clone().into(), init: Some(Box::new(Expr::Arrow(ArrowExpr { params: new_params, + body: Box::new(new_body), ..a.clone() }))), definite: Default::default(), @@ -393,17 +453,64 @@ impl ServerActions { // export async function $ACTION_myAction () {} let mut new_params: Vec = vec![]; + let mut new_body: Option = f.body.clone(); // add params from closure collected ids - for i in 0..ids_from_closure.len() { + if !ids_from_closure.is_empty() { + // First argument is the encrypted closure variables new_params.push(Param { span: DUMMY_SP, decorators: vec![], - pat: Pat::Ident( - Ident::new(format!("$$ACTION_ARG_{}", i).into(), DUMMY_SP).into(), - ), + pat: Pat::Ident(Ident::new("$$ACTION_CLOSURE_BOUND".into(), DUMMY_SP).into()), }); + + // Also prepend the decryption decl into the body. + // var [arg1, arg2, arg3] = await decryptActionBoundArgs(actionId, + // $$ACTION_CLOSURE_BOUND) + let mut pats = vec![]; + for i in 0..ids_from_closure.len() { + pats.push(Some(Pat::Ident( + Ident::new(format!("$$ACTION_ARG_{}", i).into(), DUMMY_SP).into(), + ))); + } + let decryption_decl = VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Var, + declare: false, + decls: vec![VarDeclarator { + span: DUMMY_SP, + name: Pat::Array(ArrayPat { + span: DUMMY_SP, + elems: pats, + optional: false, + type_ann: None, + }), + init: Some(Box::new(Expr::Await(AwaitExpr { + span: DUMMY_SP, + arg: Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: quote_ident!("decryptActionBoundArgs").as_callee(), + args: vec![ + generate_action_id(&self.file_name, &export_name).as_arg(), + quote_ident!("$$ACTION_CLOSURE_BOUND").as_arg(), + ], + type_args: None, + })), + }))), + definite: Default::default(), + }], + }; + + if let Some(body) = &mut new_body { + body.stmts.insert(0, decryption_decl.into()); + } else { + new_body = Some(BlockStmt { + span: DUMMY_SP, + stmts: vec![decryption_decl.into()], + }); + } } + for p in f.params.iter() { new_params.push(p.clone()); } @@ -412,9 +519,10 @@ impl ServerActions { .push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { span: DUMMY_SP, decl: FnDecl { - ident: action_ident, + ident: action_ident.clone(), function: Box::new(Function { params: new_params, + body: new_body, ..*f.take() }), declare: Default::default(), @@ -1109,8 +1217,42 @@ impl VisitMut for ServerActions { type_only: false, with: None, }))); - // Make it the first item - new.rotate_right(1); + + // Encryption and decryption only happens on the server layer. + if self.config.is_server { + // import { encryptActionBoundArgs, decryptActionBoundArgs } from + // 'private-next-rsc-action-encryption' + new.push(ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl { + span: DUMMY_SP, + specifiers: vec![ + ImportSpecifier::Named(ImportNamedSpecifier { + span: DUMMY_SP, + local: quote_ident!("encryptActionBoundArgs"), + imported: None, + is_type_only: false, + }), + ImportSpecifier::Named(ImportNamedSpecifier { + span: DUMMY_SP, + local: quote_ident!("decryptActionBoundArgs"), + imported: None, + is_type_only: false, + }), + ], + src: Box::new(Str { + span: DUMMY_SP, + value: "private-next-rsc-action-encryption".into(), + raw: None, + }), + type_only: false, + with: None, + }))); + + // Make it the first item + new.rotate_right(2); + } else { + // Make it the first item + new.rotate_right(1); + } } *stmts = new; @@ -1215,13 +1357,14 @@ fn annotate_ident_as_action( ) { // Add the proxy wrapper call `createActionProxy($$id, $$bound, myAction, // maybe_orig_action)`. + let action_id = generate_action_id(file_name, &export_name); let mut args = vec![ // $$id ExprOrSpread { spread: None, - expr: Box::new(generate_action_id(file_name, &export_name).into()), + expr: Box::new(action_id.clone().into()), }, - // myAction.$$bound = [arg1, arg2, arg3]; + // myAction.$$bound = [encryptActionBoundArgs(actionId, [arg1, arg2, arg3])]; // or myAction.$$bound = null; if there are no bound values. ExprOrSpread { spread: None, @@ -1230,7 +1373,27 @@ fn annotate_ident_as_action( } else { ArrayLit { span: DUMMY_SP, - elems: bound, + elems: vec![Some(ExprOrSpread { + spread: None, + expr: Box::new(Expr::Call(CallExpr { + span: DUMMY_SP, + callee: quote_ident!("encryptActionBoundArgs").as_callee(), + args: vec![ + ExprOrSpread { + spread: None, + expr: Box::new(action_id.into()), + }, + ExprOrSpread { + spread: None, + expr: Box::new(Expr::Array(ArrayLit { + span: DUMMY_SP, + elems: bound, + })), + }, + ], + type_args: None, + })), + })], } .into() }), diff --git a/packages/next-swc/crates/core/tests/errors/server-actions/client-graph/1/output.js b/packages/next-swc/crates/core/tests/errors/server-actions/client-graph/1/output.js index 3275be74a29a7..4f6910bb0b0e7 100644 --- a/packages/next-swc/crates/core/tests/errors/server-actions/client-graph/1/output.js +++ b/packages/next-swc/crates/core/tests/errors/server-actions/client-graph/1/output.js @@ -1,4 +1,4 @@ -/* __next_internal_client_entry_do_not_use__ default auto */ /* __next_internal_action_entry_do_not_use__ $$ACTION_0 */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_client_entry_do_not_use__ default auto */ /* __next_internal_action_entry_do_not_use__ {"6d53ce510b2e36499b8f56038817b9bad86cabb4":"$$ACTION_0"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; export default function App() { async function fn(...args) { return $$ACTION_0.apply(null, (fn.$$bound || []).concat(args)); diff --git a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/1/output.js b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/1/output.js index 11f21c43f6421..7a8fe41229e26 100644 --- a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/1/output.js +++ b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/1/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ foo */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"ab21efdafbe611287bc25c0462b1e0510d13e48b":"foo"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; export function foo() {} import { ensureServerEntryExports } from "private-next-rsc-action-validate"; ensureServerEntryExports([ diff --git a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/2/output.js b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/2/output.js index bdd1975053c9e..72edc1481ec50 100644 --- a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/2/output.js +++ b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/2/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ bar */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"ac840dcaf5e8197cb02b7f3a43c119b7a770b272":"bar"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; 'use strict'; export function bar() {} import { ensureServerEntryExports } from "private-next-rsc-action-validate"; diff --git a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/3/output.js b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/3/output.js index eab234f431c80..e3619a247013b 100644 --- a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/3/output.js +++ b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/3/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ x */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"b78c261f135a7a852508c2920bd7228020ff4bd7":"x"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; export const x = 1; import { ensureServerEntryExports } from "private-next-rsc-action-validate"; ensureServerEntryExports([ diff --git a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/4/output.js b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/4/output.js index a641bb04eca99..f8dc21b9d8ae5 100644 --- a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/4/output.js +++ b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/4/output.js @@ -1,8 +1,9 @@ -/* __next_internal_action_entry_do_not_use__ */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; export default class Component { - render() { - return null; - } + render() { + return null; + } } import { ensureServerEntryExports } from "private-next-rsc-action-validate"; ensureServerEntryExports([]); diff --git a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/5/output.js b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/5/output.js index 0e6caf6de54a8..fe4d3fb0ac3fe 100644 --- a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/5/output.js +++ b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/5/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; export * from 'foo'; import { ensureServerEntryExports } from "private-next-rsc-action-validate"; ensureServerEntryExports([]); diff --git a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/6/output.js b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/6/output.js index 795cff069312f..1e748e173380f 100644 --- a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/6/output.js +++ b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/6/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; export default (()=>{}); import { ensureServerEntryExports } from "private-next-rsc-action-validate"; ensureServerEntryExports([]); diff --git a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/7/output.js b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/7/output.js index 672090e8bd467..fafe434c1e7f2 100644 --- a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/7/output.js +++ b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/7/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ $$ACTION_1 */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"188d5d945750dc32e2c842b93c75a65763d4a922":"$$ACTION_1"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; const foo = ($$ACTION_0 = async (...args)=>$$ACTION_1.apply(null, ($$ACTION_0.$$bound || []).concat(args)), createActionProxy("188d5d945750dc32e2c842b93c75a65763d4a922", null, $$ACTION_0, $$ACTION_1), $$ACTION_0); export var $$ACTION_1 = ()=>{}; var $$ACTION_0; diff --git a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/8/output.js b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/8/output.js index 1a708ec936d85..aeb9d47f6df11 100644 --- a/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/8/output.js +++ b/packages/next-swc/crates/core/tests/errors/server-actions/server-graph/8/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ $$ACTION_1 */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"188d5d945750dc32e2c842b93c75a65763d4a922":"$$ACTION_1"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; const foo = ($$ACTION_0 = async (...args)=>$$ACTION_1.apply(null, ($$ACTION_0.$$bound || []).concat(args)), createActionProxy("188d5d945750dc32e2c842b93c75a65763d4a922", null, $$ACTION_0, $$ACTION_1), $$ACTION_0); export var $$ACTION_1 = async ()=>{ 'use strict'; diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/client/1/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/client/1/output.js index 10bc8f50094d5..eee92f539097c 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/client/1/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/client/1/output.js @@ -1,5 +1,5 @@ // app/send.ts -/* __next_internal_action_entry_do_not_use__ myAction,default */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default","e10665baac148856374b2789aceb970f66fec33e":"myAction"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; import { createServerReference } from "private-next-rsc-action-client-wrapper"; export var myAction = createServerReference("e10665baac148856374b2789aceb970f66fec33e"); export default createServerReference("c18c215a6b7cdc64bf709f3a714ffdef1bf9651d"); diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/client/2/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/client/2/output.js index 0880d7016ba62..e21c169ecfe1a 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/client/2/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/client/2/output.js @@ -1,4 +1,4 @@ // app/send.ts -/* __next_internal_action_entry_do_not_use__ foo */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"ab21efdafbe611287bc25c0462b1e0510d13e48b":"foo"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; import { createServerReference } from "private-next-rsc-action-client-wrapper"; export var foo = createServerReference("ab21efdafbe611287bc25c0462b1e0510d13e48b"); diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/client/3/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/client/3/output.js index 4b1dcc1c3ae81..b03370598668b 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/client/3/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/client/3/output.js @@ -1,4 +1,4 @@ -/* __next_internal_action_entry_do_not_use__ sampleFunction,sampleFunction2,sampleFunction3,sampleFunction4 */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"bd336abe00c3c59da66acb696fc8e151d8e54ea4":"sampleFunction","a0c73dd6f5af839e3335c6b19262ecb86cca6af4":"sampleFunction2","f03b256ee88a51700367acee3082894e25e6e7d9":"sampleFunction4","d4f2e95bc745b6500b439c0847003511748c8ece":"sampleFunction3"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; import { createServerReference } from "private-next-rsc-action-client-wrapper"; export var sampleFunction = createServerReference("bd336abe00c3c59da66acb696fc8e151d8e54ea4"); export var sampleFunction2 = createServerReference("a0c73dd6f5af839e3335c6b19262ecb86cca6af4"); diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/client/4/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/client/4/output.js index bb52e3c80391b..41fa6e3beec61 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/client/4/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/client/4/output.js @@ -1,3 +1,3 @@ -/* __next_internal_action_entry_do_not_use__ bar */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"ac840dcaf5e8197cb02b7f3a43c119b7a770b272":"bar"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; import { createServerReference } from "private-next-rsc-action-client-wrapper"; export var bar = createServerReference("ac840dcaf5e8197cb02b7f3a43c119b7a770b272"); diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/1/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/1/output.js index 678644bb9ca20..a90bc4edad633 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/1/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/1/output.js @@ -1,16 +1,20 @@ -/* __next_internal_action_entry_do_not_use__ $$ACTION_0,$$ACTION_2 */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"6d53ce510b2e36499b8f56038817b9bad86cabb4":"$$ACTION_0","9878bfa39811ca7650992850a8751f9591b6a557":"$$ACTION_2"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; import deleteFromDb from 'db'; -export function Item({ id1 , id2 }) { +export function Item({ id1, id2 }) { async function deleteItem(...args) { return $$ACTION_0.apply(null, (deleteItem.$$bound || []).concat(args)); } createActionProxy("6d53ce510b2e36499b8f56038817b9bad86cabb4", [ - id1, - id2 + encryptActionBoundArgs("6d53ce510b2e36499b8f56038817b9bad86cabb4", [ + id1, + id2 + ]) ], deleteItem, $$ACTION_0); return ; } -export async function $$ACTION_0($$ACTION_ARG_0, $$ACTION_ARG_1) { +export async function $$ACTION_0($$ACTION_CLOSURE_BOUND) { + var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("6d53ce510b2e36499b8f56038817b9bad86cabb4", $$ACTION_CLOSURE_BOUND); await deleteFromDb($$ACTION_ARG_0); await deleteFromDb($$ACTION_ARG_1); } @@ -20,12 +24,15 @@ export default function Home() { test: 'test' }; const action = ($$ACTION_1 = async (...args)=>$$ACTION_2.apply(null, ($$ACTION_1.$$bound || []).concat(args)), createActionProxy("9878bfa39811ca7650992850a8751f9591b6a557", [ - info.name, - info.test + encryptActionBoundArgs("9878bfa39811ca7650992850a8751f9591b6a557", [ + info.name, + info.test + ]) ], $$ACTION_1, $$ACTION_2), $$ACTION_1); return null; } -export var $$ACTION_2 = async ($$ACTION_ARG_0, $$ACTION_ARG_1)=>{ +export var $$ACTION_2 = async ($$ACTION_CLOSURE_BOUND)=>{ + var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("9878bfa39811ca7650992850a8751f9591b6a557", $$ACTION_CLOSURE_BOUND); console.log($$ACTION_ARG_0); console.log($$ACTION_ARG_1); }; diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/10/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/10/output.js index 639c6ad183372..3e8436d9c95a1 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/10/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/10/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ default */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; export default async function foo() {} import { ensureServerEntryExports } from "private-next-rsc-action-validate"; ensureServerEntryExports([ diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/11/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/11/output.js index 16dae3805e0e8..cea76b8da3fa0 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/11/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/11/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ default */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; export default async function $$ACTION_0() {} import { ensureServerEntryExports } from "private-next-rsc-action-validate"; ensureServerEntryExports([ diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/12/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/12/output.js index a92d3aa7830a5..5ee8474d6a6f2 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/12/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/12/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ default */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; async function foo() {} export default foo; import { ensureServerEntryExports } from "private-next-rsc-action-validate"; diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/13/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/13/output.js index 55bebae6bf91b..37475e9ead4af 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/13/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/13/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ default,bar */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"ac840dcaf5e8197cb02b7f3a43c119b7a770b272":"bar","c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; const foo = async function() {}; export default foo; const bar = async function() {}; diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/14/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/14/output.js index 7cc5f63d50992..fd8bf705474bd 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/14/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/14/output.js @@ -1,9 +1,10 @@ -/* __next_internal_action_entry_do_not_use__ foo */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"ab21efdafbe611287bc25c0462b1e0510d13e48b":"foo"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; export async function foo() { - async function bar() {} + async function bar() {} } import { ensureServerEntryExports } from "private-next-rsc-action-validate"; ensureServerEntryExports([ - foo + foo ]); createActionProxy("ab21efdafbe611287bc25c0462b1e0510d13e48b", null, foo); diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/15/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/15/output.js index 56f27cbc698b7..867cd841293fe 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/15/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/15/output.js @@ -1,10 +1,11 @@ -/* __next_internal_action_entry_do_not_use__ default */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; export default $$ACTION_0 = async (a, b)=>{ - console.log(a, b); + console.log(a, b); }; var $$ACTION_0; import { ensureServerEntryExports } from "private-next-rsc-action-validate"; ensureServerEntryExports([ - $$ACTION_0 + $$ACTION_0 ]); createActionProxy("c18c215a6b7cdc64bf709f3a714ffdef1bf9651d", null, $$ACTION_0); diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/16/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/16/output.js index f4009458e73d6..48aff3d82ad8b 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/16/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/16/output.js @@ -1,15 +1,19 @@ -/* __next_internal_action_entry_do_not_use__ $$ACTION_1,$$ACTION_2,$$ACTION_4 */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"9c0dd1f7c2b3f41d32e10f5c437de3d67ad32c6c":"$$ACTION_4","9878bfa39811ca7650992850a8751f9591b6a557":"$$ACTION_2","188d5d945750dc32e2c842b93c75a65763d4a922":"$$ACTION_1"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; import deleteFromDb from 'db'; const v1 = 'v1'; -export function Item({ id1 , id2 }) { +export function Item({ id1, id2 }) { const v2 = id2; const deleteItem = ($$ACTION_0 = async (...args)=>$$ACTION_1.apply(null, ($$ACTION_0.$$bound || []).concat(args)), createActionProxy("188d5d945750dc32e2c842b93c75a65763d4a922", [ - id1, - v2 + encryptActionBoundArgs("188d5d945750dc32e2c842b93c75a65763d4a922", [ + id1, + v2 + ]) ], $$ACTION_0, $$ACTION_1), $$ACTION_0); return ; } -export var $$ACTION_1 = async ($$ACTION_ARG_0, $$ACTION_ARG_1)=>{ +export var $$ACTION_1 = async ($$ACTION_CLOSURE_BOUND)=>{ + var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("188d5d945750dc32e2c842b93c75a65763d4a922", $$ACTION_CLOSURE_BOUND); await deleteFromDb($$ACTION_ARG_0); await deleteFromDb(v1); await deleteFromDb($$ACTION_ARG_1); @@ -20,18 +24,24 @@ const f = (x)=>{ return $$ACTION_2.apply(null, (g.$$bound || []).concat(args)); } createActionProxy("9878bfa39811ca7650992850a8751f9591b6a557", [ - x + encryptActionBoundArgs("9878bfa39811ca7650992850a8751f9591b6a557", [ + x + ]) ], g, $$ACTION_2); }; -export async function $$ACTION_2($$ACTION_ARG_0, y, ...z) { +export async function $$ACTION_2($$ACTION_CLOSURE_BOUND, y, ...z) { + var [$$ACTION_ARG_0] = await decryptActionBoundArgs("9878bfa39811ca7650992850a8751f9591b6a557", $$ACTION_CLOSURE_BOUND); return $$ACTION_ARG_0 + y + z[0]; } const g = (x)=>{ f = ($$ACTION_3 = async (...args)=>$$ACTION_4.apply(null, ($$ACTION_3.$$bound || []).concat(args)), createActionProxy("9c0dd1f7c2b3f41d32e10f5c437de3d67ad32c6c", [ - x + encryptActionBoundArgs("9c0dd1f7c2b3f41d32e10f5c437de3d67ad32c6c", [ + x + ]) ], $$ACTION_3, $$ACTION_4), $$ACTION_3); }; -export var $$ACTION_4 = async ($$ACTION_ARG_0, y, ...z)=>{ +export var $$ACTION_4 = async ($$ACTION_CLOSURE_BOUND, y, ...z)=>{ + var [$$ACTION_ARG_0] = await decryptActionBoundArgs("9c0dd1f7c2b3f41d32e10f5c437de3d67ad32c6c", $$ACTION_CLOSURE_BOUND); return $$ACTION_ARG_0 + y + z[0]; }; var $$ACTION_3; diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/17/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/17/output.js index 25f13f9621327..2768102d45678 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/17/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/17/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ foo,bar */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"ab21efdafbe611287bc25c0462b1e0510d13e48b":"foo","ac840dcaf5e8197cb02b7f3a43c119b7a770b272":"bar"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; export const foo = async ()=>{}; const bar = async ()=>{}; export { bar }; diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/18/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/18/output.js index 8c4be7568f091..1591ca40a433d 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/18/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/18/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ $$ACTION_1,$$ACTION_3 */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"188d5d945750dc32e2c842b93c75a65763d4a922":"$$ACTION_1","56a859f462d35a297c46a1bbd1e6a9058c104ab8":"$$ACTION_3"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; import deleteFromDb from 'db'; const v1 = 'v1'; export function Item({ id1, id2 }) { @@ -6,8 +7,10 @@ export function Item({ id1, id2 }) { return <> ; } -export async function $$ACTION_0($$ACTION_ARG_0, $$ACTION_ARG_1, $$ACTION_ARG_2, $$ACTION_ARG_3) { +export async function $$ACTION_0($$ACTION_CLOSURE_BOUND) { + var [$$ACTION_ARG_0, $$ACTION_ARG_1, $$ACTION_ARG_2, $$ACTION_ARG_3] = await decryptActionBoundArgs("6d53ce510b2e36499b8f56038817b9bad86cabb4", $$ACTION_CLOSURE_BOUND); await deleteFromDb($$ACTION_ARG_0); await deleteFromDb(v1); await deleteFromDb($$ACTION_ARG_1); diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/6/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/6/output.js index 2376c11fb7ff0..50ea347905162 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/6/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/6/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ $$ACTION_0 */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"6d53ce510b2e36499b8f56038817b9bad86cabb4":"$$ACTION_0"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; import f, { f1, f2 } from 'foo'; const f3 = 1; var f4; @@ -22,16 +23,19 @@ export function y(p, [p1, { p2 }], ...p3) { return $$ACTION_0.apply(null, (action.$$bound || []).concat(args)); } createActionProxy("6d53ce510b2e36499b8f56038817b9bad86cabb4", [ - f2, - f11, - p, - p1, - p2, - p3 + encryptActionBoundArgs("6d53ce510b2e36499b8f56038817b9bad86cabb4", [ + f2, + f11, + p, + p1, + p2, + p3 + ]) ], action, $$ACTION_0); return ; } -export async function $$ACTION_0($$ACTION_ARG_0, $$ACTION_ARG_1, $$ACTION_ARG_2, $$ACTION_ARG_3, $$ACTION_ARG_4, $$ACTION_ARG_5) { +export async function $$ACTION_0($$ACTION_CLOSURE_BOUND) { + var [$$ACTION_ARG_0, $$ACTION_ARG_1, $$ACTION_ARG_2, $$ACTION_ARG_3, $$ACTION_ARG_4, $$ACTION_ARG_5] = await decryptActionBoundArgs("6d53ce510b2e36499b8f56038817b9bad86cabb4", $$ACTION_CLOSURE_BOUND); const f17 = 1; if (true) { const f18 = 1; diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/7/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/7/output.js index 9b1a083e0907e..8b539ce494620 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/7/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/7/output.js @@ -1,19 +1,23 @@ -/* __next_internal_action_entry_do_not_use__ $$ACTION_0 */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"6d53ce510b2e36499b8f56038817b9bad86cabb4":"$$ACTION_0"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; import deleteFromDb from 'db'; export function Item(product, foo, bar) { async function deleteItem(...args) { return $$ACTION_0.apply(null, (deleteItem.$$bound || []).concat(args)); } createActionProxy("6d53ce510b2e36499b8f56038817b9bad86cabb4", [ - product.id, - product?.foo, - product.bar.baz, - product, - foo, - bar + encryptActionBoundArgs("6d53ce510b2e36499b8f56038817b9bad86cabb4", [ + product.id, + product?.foo, + product.bar.baz, + product, + foo, + bar + ]) ], deleteItem, $$ACTION_0); return ; } -export async function $$ACTION_0($$ACTION_ARG_0, $$ACTION_ARG_1, $$ACTION_ARG_2, $$ACTION_ARG_3, $$ACTION_ARG_4, $$ACTION_ARG_5) { +export async function $$ACTION_0($$ACTION_CLOSURE_BOUND) { + var [$$ACTION_ARG_0, $$ACTION_ARG_1, $$ACTION_ARG_2, $$ACTION_ARG_3, $$ACTION_ARG_4, $$ACTION_ARG_5] = await decryptActionBoundArgs("6d53ce510b2e36499b8f56038817b9bad86cabb4", $$ACTION_CLOSURE_BOUND); await deleteFromDb($$ACTION_ARG_3.id, $$ACTION_ARG_3?.foo, $$ACTION_ARG_3.bar.baz, $$ACTION_ARG_3[$$ACTION_ARG_4, $$ACTION_ARG_5]); } diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/8/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/8/output.js index ada9615ff1680..93358366e844f 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/8/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/8/output.js @@ -1,4 +1,5 @@ -/* __next_internal_action_entry_do_not_use__ $$ACTION_0 */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"6d53ce510b2e36499b8f56038817b9bad86cabb4":"$$ACTION_0"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; async function myAction(...args) { return $$ACTION_0.apply(null, (myAction.$$bound || []).concat(args)); } diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/server/9/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/server/9/output.js index 1f354a4dc7fbd..38682c5fd5672 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/server/9/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/server/9/output.js @@ -1,5 +1,6 @@ // app/send.ts -/* __next_internal_action_entry_do_not_use__ foo,baz,default */ import { createActionProxy } from "private-next-rsc-action-proxy"; +/* __next_internal_action_entry_do_not_use__ {"c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default","ab21efdafbe611287bc25c0462b1e0510d13e48b":"foo","050e3854b72b19e3c7e3966a67535543a90bf7e0":"baz"} */ import { createActionProxy } from "private-next-rsc-action-proxy"; +import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; async function foo() {} export { foo }; async function bar() {} diff --git a/packages/next-swc/crates/napi/Cargo.toml b/packages/next-swc/crates/napi/Cargo.toml index 255979b3bb1d7..303a3afb4157f 100644 --- a/packages/next-swc/crates/napi/Cargo.toml +++ b/packages/next-swc/crates/napi/Cargo.toml @@ -76,6 +76,7 @@ turbopack-binding = { workspace = true, features = [ "__turbopack_ecmascript_hmr_protocol", ] } url = {workspace = true} +urlencoding = {workspace = true} [target.'cfg(not(all(target_os = "linux", target_env = "musl", target_arch = "aarch64")))'.dependencies] turbopack-binding = { workspace = true, features = ["__turbo_tasks_malloc"] } diff --git a/packages/next-swc/crates/napi/src/next_api/project.rs b/packages/next-swc/crates/napi/src/next_api/project.rs index ecc72bdbf1be8..387e8f40a79fe 100644 --- a/packages/next-swc/crates/napi/src/next_api/project.rs +++ b/packages/next-swc/crates/napi/src/next_api/project.rs @@ -617,10 +617,11 @@ pub fn project_update_info_subscribe( #[derive(Debug)] #[napi(object)] pub struct StackFrame { + pub column: Option, pub file: String, - pub method_name: Option, + pub is_server: bool, pub line: u32, - pub column: Option, + pub method_name: Option, } #[napi] @@ -633,7 +634,7 @@ pub async fn project_trace_source( .run_once(async move { let file = match Url::parse(&frame.file) { Ok(url) => match url.scheme() { - "file" => url.path().to_string(), + "file" => urlencoding::decode(url.path())?.to_string(), _ => bail!("Unknown url scheme"), }, Err(_) => frame.file.to_string(), @@ -641,8 +642,8 @@ pub async fn project_trace_source( let Some(chunk_base) = file.strip_prefix( &(format!( - "{}/{}", - project.container.project().await?.root_path, + "{}/{}/", + project.container.project().await?.project_path, project.container.project().dist_dir().await? )), ) else { @@ -650,32 +651,29 @@ pub async fn project_trace_source( return Ok(None); }; - let chunk_path = format!( - "{}{}", + let path = if frame.is_server { + project + .container + .project() + .node_root() + .join(chunk_base.to_owned()) + } else { project .container .project() .client_relative_path() - .await? - .path, - chunk_base - ); - - let path = project - .container - .project() - .client_root() - .fs() - .root() - .join(chunk_path); + .join(chunk_base.to_owned()) + }; - let Some(generatable): Option>> = - Vc::try_resolve_sidecast(project.container.get_versioned_content(path)).await? + let Some(versioned) = Vc::try_resolve_sidecast::>( + project.container.get_versioned_content(path), + ) + .await? else { - return Ok(None); + bail!("Could not GenerateSourceMap") }; - let map = generatable + let map = versioned .generate_source_map() .await? .context("Chunk is missing a sourcemap")?; @@ -700,6 +698,7 @@ pub async fn project_trace_source( method_name: token.name, line: token.original_line as u32, column: Some(token.original_column as u32), + is_server: frame.is_server, })) }) .await @@ -719,12 +718,14 @@ pub async fn project_get_source_for_asset( .container .project() .project_path() + .fs() + .root() .join(file_path.to_string()) .read() .await?; let FileContent::Content(source_content) = source_content else { - return Ok(None); + bail!("Cannot find source for asset {}", file_path); }; Ok(Some(source_content.content().to_str()?.to_string())) diff --git a/packages/next-swc/crates/next-api/src/app.rs b/packages/next-swc/crates/next-api/src/app.rs index 7cd5bfd1b2163..34201d77533ec 100644 --- a/packages/next-swc/crates/next-api/src/app.rs +++ b/packages/next-swc/crates/next-api/src/app.rs @@ -168,6 +168,7 @@ impl AppProject { self.project().client_compile_time_info(), self.client_module_options_context(), self.client_resolve_options_context(), + Vc::cell("client".to_string()), ) } @@ -230,6 +231,7 @@ impl AppProject { self.project().server_compile_time_info(), self.rsc_module_options_context(), self.rsc_resolve_options_context(), + Vc::cell("rsc".to_string()), ) } @@ -255,6 +257,7 @@ impl AppProject { self.project().edge_compile_time_info(), self.rsc_module_options_context(), self.edge_rsc_resolve_options_context(), + Vc::cell("edge_rsc".to_string()), ) } @@ -265,6 +268,7 @@ impl AppProject { self.project().client_compile_time_info(), self.client_module_options_context(), self.client_resolve_options_context(), + Vc::cell("client".to_string()), ) } @@ -298,6 +302,7 @@ impl AppProject { self.project().server_compile_time_info(), self.ssr_module_options_context(), self.ssr_resolve_options_context(), + Vc::cell("ssr".to_string()), ) } @@ -523,7 +528,7 @@ impl AppEndpoint { .ident() .with_modifier(Vc::cell("client_shared_chunks".to_string())), this.app_project.client_runtime_entries(), - this.app_project.project().app_client_chunking_context(), + this.app_project.project().client_chunking_context(), ); let mut client_shared_chunks_paths = vec![]; @@ -577,8 +582,8 @@ impl AppEndpoint { if ssr_and_client { let client_references_chunks = get_app_client_references_chunks( client_reference_types, - this.app_project.project().app_client_chunking_context(), - this.app_project.project().app_ssr_chunking_context(), + this.app_project.project().client_chunking_context(), + this.app_project.project().server_chunking_context(), ); let client_references_chunks_ref = client_references_chunks.await?; @@ -649,8 +654,8 @@ impl AppEndpoint { app_entry.original_name.clone(), client_references, client_references_chunks, - this.app_project.project().app_client_chunking_context(), - Vc::upcast(this.app_project.project().app_ssr_chunking_context()), + this.app_project.project().client_chunking_context(), + Vc::upcast(this.app_project.project().server_chunking_context()), this.app_project .project() .next_config() @@ -750,7 +755,7 @@ impl AppEndpoint { let endpoint_output = match app_entry.config.await?.runtime.unwrap_or_default() { NextRuntime::Edge => { // create edge chunks - let chunking_context = this.app_project.project().edge_rsc_chunking_context(); + let chunking_context = this.app_project.project().edge_chunking_context(); let mut evaluatable_assets = this .app_project .edge_rsc_runtime_entries() @@ -904,7 +909,7 @@ impl AppEndpoint { &app_entry.original_name, NextRuntime::NodeJs, Vc::upcast(this.app_project.rsc_module_context()), - Vc::upcast(this.app_project.project().rsc_chunking_context()), + Vc::upcast(this.app_project.project().server_chunking_context()), this.app_project .project() .next_config() @@ -919,7 +924,7 @@ impl AppEndpoint { let rsc_chunk = this .app_project .project() - .rsc_chunking_context() + .server_chunking_context() .entry_chunk_group( server_path.join(format!( "app{original_name}.js", @@ -948,7 +953,7 @@ impl AppEndpoint { let dynamic_import_modules = collect_next_dynamic_imports(app_entry.rsc_entry).await?; let dynamic_import_entries = collect_chunk_group( - this.app_project.project().rsc_chunking_context(), + this.app_project.project().server_chunking_context(), dynamic_import_modules, availability_info, ) diff --git a/packages/next-swc/crates/next-api/src/middleware.rs b/packages/next-swc/crates/next-api/src/middleware.rs index ba5f17524f777..e8e613dfd0e72 100644 --- a/packages/next-swc/crates/next-api/src/middleware.rs +++ b/packages/next-swc/crates/next-api/src/middleware.rs @@ -86,7 +86,7 @@ impl MiddlewareEndpoint { }; evaluatable_assets.push(evaluatable); - let edge_chunking_context = self.project.edge_middleware_chunking_context(); + let edge_chunking_context = self.project.edge_chunking_context(); let edge_files = edge_chunking_context .evaluated_chunk_group(module.ident(), Vc::cell(evaluatable_assets)); diff --git a/packages/next-swc/crates/next-api/src/pages.rs b/packages/next-swc/crates/next-api/src/pages.rs index 2275513a81fd5..1dcbfa4ace4ea 100644 --- a/packages/next-swc/crates/next-api/src/pages.rs +++ b/packages/next-swc/crates/next-api/src/pages.rs @@ -264,6 +264,7 @@ impl PagesProject { self.project().client_compile_time_info(), self.client_module_options_context(), self.client_resolve_options_context(), + Vc::cell("client".to_string()), ) } @@ -303,6 +304,7 @@ impl PagesProject { self.project().client_compile_time_info(), self.client_module_options_context(), self.client_resolve_options_context(), + Vc::cell("client".to_string()), )) } @@ -313,6 +315,7 @@ impl PagesProject { self.project().server_compile_time_info(), self.ssr_module_options_context(), self.ssr_resolve_options_context(), + Vc::cell("ssr".to_string()), ) } @@ -323,6 +326,7 @@ impl PagesProject { self.project().server_compile_time_info(), self.ssr_data_module_options_context(), self.ssr_resolve_options_context(), + Vc::cell("ssr_data".to_string()), ) } @@ -333,6 +337,7 @@ impl PagesProject { self.project().edge_compile_time_info(), self.ssr_module_options_context(), self.edge_ssr_resolve_options_context(), + Vc::cell("edge_ssr".to_string()), ) } @@ -343,6 +348,7 @@ impl PagesProject { self.project().edge_compile_time_info(), self.ssr_data_module_options_context(), self.edge_ssr_resolve_options_context(), + Vc::cell("edge_ssr_data".to_string()), ) } @@ -677,8 +683,8 @@ impl PageEndpoint { this.pages_project.project().project_path(), this.pages_project.ssr_module_context(), this.pages_project.edge_ssr_module_context(), - this.pages_project.project().ssr_chunking_context(), - this.pages_project.project().edge_ssr_chunking_context(), + this.pages_project.project().server_chunking_context(), + this.pages_project.project().edge_chunking_context(), this.pages_project.ssr_runtime_entries(), this.pages_project.edge_ssr_runtime_entries(), )) @@ -696,10 +702,8 @@ impl PageEndpoint { this.pages_project.project().project_path(), this.pages_project.ssr_data_module_context(), this.pages_project.edge_ssr_data_module_context(), - this.pages_project.project().ssr_data_chunking_context(), - this.pages_project - .project() - .edge_ssr_data_chunking_context(), + this.pages_project.project().server_chunking_context(), + this.pages_project.project().edge_chunking_context(), this.pages_project.ssr_data_runtime_entries(), this.pages_project.edge_ssr_data_runtime_entries(), )) @@ -717,8 +721,8 @@ impl PageEndpoint { this.pages_project.project().project_path(), this.pages_project.ssr_module_context(), this.pages_project.edge_ssr_module_context(), - this.pages_project.project().ssr_chunking_context(), - this.pages_project.project().edge_ssr_chunking_context(), + this.pages_project.project().server_chunking_context(), + this.pages_project.project().edge_chunking_context(), this.pages_project.ssr_runtime_entries(), this.pages_project.edge_ssr_runtime_entries(), )) diff --git a/packages/next-swc/crates/next-api/src/project.rs b/packages/next-swc/crates/next-api/src/project.rs index fe445e59adde0..1bc9e00e65325 100644 --- a/packages/next-swc/crates/next-api/src/project.rs +++ b/packages/next-swc/crates/next-api/src/project.rs @@ -34,7 +34,6 @@ use turbopack_binding::{ build::BuildChunkingContext, core::{ changed::content_changed, - chunk::ChunkingContext, compile_time_info::CompileTimeInfo, context::AssetContext, diagnostics::DiagnosticExt, @@ -270,13 +269,13 @@ impl ProjectContainer { pub struct Project { /// A root path from which all files must be nested under. Trying to access /// a file outside this root will fail. Think of this as a chroot. - pub root_path: String, + root_path: String, /// A path where to emit the build outputs. next.config.js's distDir. dist_dir: String, /// A path inside the root_path which contains the app/pages directories. - project_path: String, + pub project_path: String, /// Whether to watch the filesystem for file changes. watch: bool, @@ -370,7 +369,7 @@ impl Project { } #[turbo_tasks::function] - async fn node_fs(self: Vc) -> Result>> { + pub async fn node_fs(self: Vc) -> Result>> { let this = self.await?; let disk_fs = DiskFileSystem::new("node".to_string(), this.project_path.clone()); disk_fs.await?.start_watching_with_invalidation_reason()?; @@ -520,14 +519,7 @@ impl Project { } #[turbo_tasks::function] - pub(super) fn app_client_chunking_context( - self: Vc, - ) -> Vc> { - self.client_chunking_context().with_layer("app".to_string()) - } - - #[turbo_tasks::function] - fn server_chunking_context(self: Vc) -> Vc { + pub(super) fn server_chunking_context(self: Vc) -> Vc { get_server_chunking_context( self.project_path(), self.node_root(), @@ -538,7 +530,7 @@ impl Project { } #[turbo_tasks::function] - fn edge_chunking_context(self: Vc) -> Vc> { + pub(super) fn edge_chunking_context(self: Vc) -> Vc> { get_edge_chunking_context( self.project_path(), self.node_root(), @@ -625,60 +617,6 @@ impl Project { Ok(Default::default()) } - #[turbo_tasks::function] - pub(super) fn ssr_chunking_context(self: Vc) -> Vc { - self.server_chunking_context().with_layer("ssr".to_string()) - } - - #[turbo_tasks::function] - pub(super) fn app_ssr_chunking_context(self: Vc) -> Vc { - self.server_chunking_context() - .with_layer("app ssr".to_string()) - } - - #[turbo_tasks::function] - pub(super) fn edge_ssr_chunking_context( - self: Vc, - ) -> Vc> { - self.edge_chunking_context() - .with_layer("edge ssr".to_string()) - } - - #[turbo_tasks::function] - pub(super) fn ssr_data_chunking_context(self: Vc) -> Vc { - self.server_chunking_context() - .with_layer("ssr data".to_string()) - } - - #[turbo_tasks::function] - pub(super) fn edge_ssr_data_chunking_context( - self: Vc, - ) -> Vc> { - self.edge_chunking_context() - .with_layer("edge ssr data".to_string()) - } - - #[turbo_tasks::function] - pub(super) fn rsc_chunking_context(self: Vc) -> Vc { - self.server_chunking_context().with_layer("rsc".to_string()) - } - - #[turbo_tasks::function] - pub(super) fn edge_rsc_chunking_context( - self: Vc, - ) -> Vc> { - self.edge_chunking_context() - .with_layer("edge rsc".to_string()) - } - - #[turbo_tasks::function] - pub(super) fn edge_middleware_chunking_context( - self: Vc, - ) -> Vc> { - self.edge_chunking_context() - .with_layer("middleware".to_string()) - } - /// Scans the app/pages directories for entry points files (matching the /// provided page_extensions). #[turbo_tasks::function] @@ -766,6 +704,7 @@ impl Project { self.next_config(), self.execution_context(), ), + Vc::cell("middleware".to_string()), )) } diff --git a/packages/next-swc/crates/next-build/src/next_app/app_entries.rs b/packages/next-swc/crates/next-build/src/next_app/app_entries.rs index c1d04ff2bf752..d71b329542f89 100644 --- a/packages/next-swc/crates/next-build/src/next_app/app_entries.rs +++ b/packages/next-swc/crates/next-build/src/next_app/app_entries.rs @@ -111,6 +111,7 @@ pub async fn get_app_entries( client_compile_time_info, client_module_options_context, client_resolve_options_context, + Vc::cell("client".to_string()), ); let ssr_resolve_options_context = get_server_resolve_options_context( @@ -133,6 +134,7 @@ pub async fn get_app_entries( server_compile_time_info, ssr_module_options_context, ssr_resolve_options_context, + Vc::cell("ssr".to_string()), ); const ECMASCRIPT_CLIENT_TRANSITION_NAME: &str = "next-ecmascript-client-reference"; @@ -179,6 +181,7 @@ pub async fn get_app_entries( server_compile_time_info, rsc_module_options_context, rsc_resolve_options_context, + Vc::cell("rsc".to_string()), ); let entries = entrypoints @@ -221,6 +224,7 @@ pub async fn get_app_entries( client_compile_time_info, client_module_options_context, client_resolve_options_context, + Vc::cell("client".to_string()), ); let client_runtime_entries = get_client_runtime_entries( diff --git a/packages/next-swc/crates/next-build/src/next_build.rs b/packages/next-swc/crates/next-build/src/next_build.rs index 4828913866c2f..dc080ea7c98b7 100644 --- a/packages/next-swc/crates/next-build/src/next_build.rs +++ b/packages/next-swc/crates/next-build/src/next_build.rs @@ -33,7 +33,6 @@ use turbopack_binding::{ cli_utils::issue::{ConsoleUi, LogOptions}, core::{ asset::Asset, - chunk::ChunkingContext, environment::ServerAddr, issue::{handle_issues, IssueReporter, IssueSeverity}, output::{OutputAsset, OutputAssets}, @@ -129,7 +128,7 @@ pub(crate) async fn next_build(options: TransientInstance) -> Resu let execution_context = ExecutionContext::new(project_root, node_execution_chunking_context, env); - let next_config = load_next_config(execution_context.with_layer("next_config".to_string())); + let next_config = load_next_config(execution_context); let mode = NextMode::Build; @@ -278,11 +277,6 @@ pub(crate) async fn next_build(options: TransientInstance) -> Resu next_config.computed_asset_prefix(), server_compile_time_info.environment(), ); - // TODO(alexkirsz) This should be the same chunking context. The layer should - // be applied on the AssetContext level instead. - let rsc_chunking_context = server_chunking_context.with_layer("rsc".to_string()); - let ssr_chunking_context = server_chunking_context.with_layer("ssr".to_string()); - let mut all_chunks = vec![]; let mut build_manifest: BuildManifest = Default::default(); @@ -297,7 +291,7 @@ pub(crate) async fn next_build(options: TransientInstance) -> Resu compute_page_entries_chunks( &page_entries, client_chunking_context, - ssr_chunking_context, + server_chunking_context, node_root, &pages_manifest_dir_path, &client_relative_path_ref, @@ -321,7 +315,7 @@ pub(crate) async fn next_build(options: TransientInstance) -> Resu let app_client_references_chunks = get_app_client_references_chunks( app_client_reference_tys, client_chunking_context, - ssr_chunking_context, + server_chunking_context, ); let app_client_references_chunks_ref = app_client_references_chunks.await?; @@ -340,9 +334,9 @@ pub(crate) async fn next_build(options: TransientInstance) -> Resu &app_entries, app_client_references, app_client_references_chunks, - rsc_chunking_context, + server_chunking_context, client_chunking_context, - Vc::upcast(ssr_chunking_context), + Vc::upcast(server_chunking_context), node_root, client_relative_path, &app_paths_manifest_dir_path, diff --git a/packages/next-swc/crates/next-build/src/next_pages/page_entries.rs b/packages/next-swc/crates/next-build/src/next_pages/page_entries.rs index 2fad04e0ae0da..cf5e98d05dadd 100644 --- a/packages/next-swc/crates/next-build/src/next_pages/page_entries.rs +++ b/packages/next-swc/crates/next-build/src/next_pages/page_entries.rs @@ -100,6 +100,7 @@ pub async fn get_page_entries( client_compile_time_info, client_module_options_context, client_resolve_options_context, + Vc::cell("client".to_string()), ); let transitions = Vc::cell( @@ -116,6 +117,7 @@ pub async fn get_page_entries( client_compile_time_info, client_module_options_context, client_resolve_options_context, + Vc::cell("client".to_string()), )); let client_runtime_entries = get_client_runtime_entries( @@ -147,6 +149,7 @@ pub async fn get_page_entries( server_compile_time_info, ssr_module_options_context, ssr_resolve_options_context, + Vc::cell("ssr".to_string()), )); let ssr_runtime_entries = get_server_runtime_entries(ssr_ty, mode); diff --git a/packages/next-swc/crates/next-core/src/app_render/mod.rs b/packages/next-swc/crates/next-core/src/app_render/mod.rs deleted file mode 100644 index 33e3c04b6d2cd..0000000000000 --- a/packages/next-swc/crates/next-core/src/app_render/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::collections::HashMap; - -use turbo_tasks::Vc; -use turbopack_binding::turbo::tasks_fs::FileSystemPath; - -pub mod next_server_component_transition; - -#[turbo_tasks::value(shared)] -pub struct LayoutSegment { - pub files: HashMap>, - pub target: Vc, -} - -#[turbo_tasks::value(transparent)] -pub struct LayoutSegments(Vec>); diff --git a/packages/next-swc/crates/next-core/src/app_render/next_server_component_transition.rs b/packages/next-swc/crates/next-core/src/app_render/next_server_component_transition.rs deleted file mode 100644 index 4e35d4f2aefba..0000000000000 --- a/packages/next-swc/crates/next-core/src/app_render/next_server_component_transition.rs +++ /dev/null @@ -1,81 +0,0 @@ -use anyhow::{bail, Result}; -use turbo_tasks::Vc; -use turbopack_binding::{ - turbo::tasks_fs::FileSystemPath, - turbopack::{ - core::{compile_time_info::CompileTimeInfo, module::Module}, - ecmascript::chunk::EcmascriptChunkPlaceable, - turbopack::{ - module_options::ModuleOptionsContext, resolve_options_context::ResolveOptionsContext, - transition::Transition, ModuleAssetContext, - }, - }, -}; - -use crate::next_client_component::{ - with_chunking_context_scope_asset::WithChunkingContextScopeAsset, - with_client_chunks::WithClientChunksAsset, -}; - -#[turbo_tasks::value(shared)] -pub struct NextServerComponentTransition { - pub rsc_compile_time_info: Vc, - pub rsc_module_options_context: Vc, - pub rsc_resolve_options_context: Vc, - pub server_root: Vc, -} - -#[turbo_tasks::value_impl] -impl Transition for NextServerComponentTransition { - #[turbo_tasks::function] - fn process_compile_time_info( - &self, - _compile_time_info: Vc, - ) -> Vc { - self.rsc_compile_time_info - } - - #[turbo_tasks::function] - fn process_module_options_context( - &self, - _context: Vc, - ) -> Vc { - self.rsc_module_options_context - } - - #[turbo_tasks::function] - fn process_resolve_options_context( - &self, - _context: Vc, - ) -> Vc { - self.rsc_resolve_options_context - } - - #[turbo_tasks::function] - async fn process_module( - &self, - module: Vc>, - _context: Vc, - ) -> Result>> { - let Some(asset) = - Vc::try_resolve_sidecast::>(module).await? - else { - bail!("Not an ecmascript module"); - }; - - Ok(Vc::upcast( - WithChunkingContextScopeAsset { - asset: Vc::upcast( - WithClientChunksAsset { - asset, - // next.js code already adds _next prefix - server_root: self.server_root.join("_next".to_string()), - } - .cell(), - ), - layer: "rsc".to_string(), - } - .cell(), - )) - } -} diff --git a/packages/next-swc/crates/next-core/src/app_structure.rs b/packages/next-swc/crates/next-core/src/app_structure.rs index 17e46688224b6..5db373b0f97b9 100644 --- a/packages/next-swc/crates/next-core/src/app_structure.rs +++ b/packages/next-swc/crates/next-core/src/app_structure.rs @@ -168,6 +168,15 @@ pub struct Metadata { pub open_graph: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub sitemap: Option, + // The page indicates where the metadata is defined and captured. + // The steps for capturing metadata (get_directory_tree) and constructing + // LoaderTree (directory_tree_to_entrypoints) is separated, + // and child loader tree can trickle down metadata when clone / merge components calculates + // the actual path incorrectly with fillMetadataSegment. + // + // This is only being used for the static metadata files. + #[serde(skip_serializing_if = "Option::is_none")] + pub base_page: Option, } impl Metadata { @@ -178,12 +187,14 @@ impl Metadata { twitter, open_graph, sitemap, + base_page, } = self; icon.is_empty() && apple.is_empty() && twitter.is_empty() && open_graph.is_empty() && sitemap.is_none() + && base_page.is_none() } fn merge(a: &Self, b: &Self) -> Self { @@ -198,6 +209,7 @@ impl Metadata { .copied() .collect(), sitemap: a.sitemap.or(b.sitemap), + base_page: a.base_page.as_ref().or(b.base_page.as_ref()).cloned(), } } } @@ -709,9 +721,42 @@ async fn directory_tree_to_entrypoints_internal( let subdirectories = &directory_tree.subdirectories; let components = directory_tree.components.await?; + // Capture the current page for the metadata to calculate segment relative to + // the corresponding page for the static metadata files. + /* + let metadata = Metadata { + base_page: Some(app_page.clone()), + ..components.metadata.clone() + }; */ + + let components = if components.metadata.base_page.is_some() { + components + } else { + (Components { + metadata: Metadata { + base_page: Some(app_page.clone()), + ..components.metadata.clone() + }, + ..*components + }) + .cell() + .await? + }; + let current_level_is_parallel_route = is_parallel_route(&directory_name); if let Some(page) = components.page { + // When resolving metadata with corresponding module + // (https://github.com/vercel/next.js/blob/aa1ee5995cdd92cc9a2236ce4b6aa2b67c9d32b2/packages/next/src/lib/metadata/resolve-metadata.ts#L340) + // layout takes precedence over page (https://github.com/vercel/next.js/blob/aa1ee5995cdd92cc9a2236ce4b6aa2b67c9d32b2/packages/next/src/server/lib/app-dir-module.ts#L22) + // If the component have layout and page both, do not attach same metadata to + // the page. + let metadata = if components.layout.is_some() { + Default::default() + } else { + components.metadata.clone() + }; + add_app_page( app_dir, &mut result, @@ -723,6 +768,7 @@ async fn directory_tree_to_entrypoints_internal( parallel_routes: IndexMap::new(), components: Components { page: Some(page), + metadata, ..Default::default() } .cell(), @@ -740,6 +786,7 @@ async fn directory_tree_to_entrypoints_internal( parallel_routes: IndexMap::new(), components: Components { page: Some(page), + metadata, ..Default::default() } .cell(), @@ -816,6 +863,7 @@ async fn directory_tree_to_entrypoints_internal( twitter, open_graph, sitemap, + base_page: _, } = &components.metadata; for meta in sitemap diff --git a/packages/next-swc/crates/next-core/src/lib.rs b/packages/next-swc/crates/next-core/src/lib.rs index 32b4ee101b05e..52248f6af892a 100644 --- a/packages/next-swc/crates/next-core/src/lib.rs +++ b/packages/next-swc/crates/next-core/src/lib.rs @@ -6,7 +6,6 @@ #![feature(arbitrary_self_types)] #![feature(async_fn_in_trait)] -mod app_render; mod app_segment_config; pub mod app_structure; mod babel; @@ -19,7 +18,6 @@ pub mod mode; pub mod next_app; mod next_build; pub mod next_client; -mod next_client_component; pub mod next_client_reference; pub mod next_config; pub mod next_dynamic; diff --git a/packages/next-swc/crates/next-core/src/loader_tree.rs b/packages/next-swc/crates/next-core/src/loader_tree.rs index 90a58f207778f..5c471de68ef71 100644 --- a/packages/next-swc/crates/next-core/src/loader_tree.rs +++ b/packages/next-swc/crates/next-core/src/loader_tree.rs @@ -159,7 +159,7 @@ impl LoaderTreeBuilder { &mut self, app_page: &AppPage, metadata: &Metadata, - global_metadata: &GlobalMetadata, + global_metadata: Option<&GlobalMetadata>, ) -> Result<()> { if metadata.is_empty() { return Ok(()); @@ -170,14 +170,28 @@ impl LoaderTreeBuilder { twitter, open_graph, sitemap: _, + base_page, } = metadata; - let GlobalMetadata { - favicon: _, - manifest, - robots: _, - } = global_metadata; - + let app_page = base_page.as_ref().unwrap_or(app_page); self.loader_tree_code += " metadata: {"; + + // naively convert metadataitem -> metadatawithaltitem to iterate along with + // other icon items + let icon = if let Some(favicon) = global_metadata.and_then(|m| m.favicon) { + let item = match favicon { + MetadataItem::Static { path } => MetadataWithAltItem::Static { + path, + alt_path: None, + }, + MetadataItem::Dynamic { path } => MetadataWithAltItem::Dynamic { path }, + }; + let mut item = vec![item]; + item.extend(icon.iter()); + item + } else { + icon.clone() + }; + self.write_metadata_items(app_page, "icon", icon.iter()) .await?; self.write_metadata_items(app_page, "apple", apple.iter()) @@ -186,7 +200,11 @@ impl LoaderTreeBuilder { .await?; self.write_metadata_items(app_page, "openGraph", open_graph.iter()) .await?; - self.write_metadata_manifest(*manifest).await?; + + if let Some(global_metadata) = global_metadata { + self.write_metadata_manifest(global_metadata.manifest) + .await?; + } self.loader_tree_code += " },"; Ok(()) } @@ -347,7 +365,7 @@ impl LoaderTreeBuilder { } #[async_recursion] - async fn walk_tree(&mut self, loader_tree: Vc) -> Result<()> { + async fn walk_tree(&mut self, loader_tree: Vc, root: bool) -> Result<()> { use std::fmt::Write; let LoaderTree { @@ -366,7 +384,7 @@ impl LoaderTreeBuilder { // add parallel_routes for (key, ¶llel_route) in parallel_routes.iter() { write!(self.loader_tree_code, "{key}: ", key = StringifyJs(key))?; - self.walk_tree(parallel_route).await?; + self.walk_tree(parallel_route, false).await?; writeln!(self.loader_tree_code, ",")?; } writeln!(self.loader_tree_code, "}}, {{")?; @@ -393,14 +411,23 @@ impl LoaderTreeBuilder { .await?; self.write_component(ComponentType::NotFound, *not_found) .await?; - self.write_metadata(app_page, metadata, &*global_metadata.await?) - .await?; + + // Ensure global metadata being written only once at the root level + // Otherwise child pages will have redundant metadata + let global_metadata = &*global_metadata.await?; + self.write_metadata( + app_page, + metadata, + if root { Some(global_metadata) } else { None }, + ) + .await?; + write!(self.loader_tree_code, "}}]")?; Ok(()) } async fn build(mut self, loader_tree: Vc) -> Result { - self.walk_tree(loader_tree).await?; + self.walk_tree(loader_tree, true).await?; Ok(LoaderTreeModule { imports: self.imports, loader_tree_code: self.loader_tree_code, diff --git a/packages/next-swc/crates/next-core/src/next_client/transition.rs b/packages/next-swc/crates/next-core/src/next_client/transition.rs index 49ef10d6e34d3..68504099af329 100644 --- a/packages/next-swc/crates/next-core/src/next_client/transition.rs +++ b/packages/next-swc/crates/next-core/src/next_client/transition.rs @@ -34,6 +34,11 @@ pub struct NextClientTransition { #[turbo_tasks::value_impl] impl Transition for NextClientTransition { + #[turbo_tasks::function] + fn process_layer(self: Vc, _layer: Vc) -> Vc { + Vc::cell("client".to_string()) + } + #[turbo_tasks::function] fn process_compile_time_info( &self, diff --git a/packages/next-swc/crates/next-core/src/next_client_component/mod.rs b/packages/next-swc/crates/next-core/src/next_client_component/mod.rs deleted file mode 100644 index b451ca492fc4d..0000000000000 --- a/packages/next-swc/crates/next-core/src/next_client_component/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod server_to_client_transition; -pub mod ssr_client_module_transition; -pub mod with_chunking_context_scope_asset; -pub mod with_client_chunks; diff --git a/packages/next-swc/crates/next-core/src/next_client_component/server_to_client_transition.rs b/packages/next-swc/crates/next-core/src/next_client_component/server_to_client_transition.rs deleted file mode 100644 index b71875611d401..0000000000000 --- a/packages/next-swc/crates/next-core/src/next_client_component/server_to_client_transition.rs +++ /dev/null @@ -1,75 +0,0 @@ -use anyhow::Result; -use indexmap::indexmap; -use turbo_tasks::{Value, Vc}; -use turbopack_binding::turbopack::{ - core::{ - context::AssetContext, - module::Module, - reference_type::{EntryReferenceSubType, ReferenceType}, - source::Source, - }, - turbopack::{transition::Transition, ModuleAssetContext}, -}; - -use crate::embed_js::next_asset; - -#[turbo_tasks::value(shared)] -pub struct NextServerToClientTransition { - pub ssr: bool, - pub edge: bool, -} - -#[turbo_tasks::value_impl] -impl Transition for NextServerToClientTransition { - #[turbo_tasks::function] - async fn process( - self: Vc, - source: Vc>, - context: Vc, - _reference_type: Value, - ) -> Result>> { - let this = self.await?; - let context = self.process_context(context); - let client_chunks = context - .with_transition("next-client-chunks".to_string()) - .process( - source, - Value::new(ReferenceType::Entry( - EntryReferenceSubType::AppClientComponent, - )), - ); - - Ok(match this.ssr { - true => { - let internal_source = next_asset("entry/app/server-to-client-ssr.tsx".to_string()); - let transition = if this.edge { - "next-edge-ssr-client-module" - } else { - "next-ssr-client-module" - }; - let client_module = context.with_transition(transition.to_string()).process( - source, - Value::new(ReferenceType::Entry( - EntryReferenceSubType::AppClientComponent, - )), - ); - context.process( - internal_source, - Value::new(ReferenceType::Internal(Vc::cell(indexmap! { - "CLIENT_MODULE".to_string() => client_module, - "CLIENT_CHUNKS".to_string() => client_chunks, - }))), - ) - } - false => { - let internal_source = next_asset("entry/app/server-to-client.tsx".to_string()); - context.process( - internal_source, - Value::new(ReferenceType::Internal(Vc::cell(indexmap! { - "CLIENT_CHUNKS".to_string() => client_chunks, - }))), - ) - } - }) - } -} diff --git a/packages/next-swc/crates/next-core/src/next_client_component/ssr_client_module_transition.rs b/packages/next-swc/crates/next-core/src/next_client_component/ssr_client_module_transition.rs deleted file mode 100644 index 61c630d8c632b..0000000000000 --- a/packages/next-swc/crates/next-core/src/next_client_component/ssr_client_module_transition.rs +++ /dev/null @@ -1,68 +0,0 @@ -use anyhow::Result; -use turbo_tasks::Vc; -use turbopack_binding::turbopack::{ - core::{compile_time_info::CompileTimeInfo, module::Module}, - turbopack::{ - ecmascript::chunk::EcmascriptChunkPlaceable, module_options::ModuleOptionsContext, - resolve_options_context::ResolveOptionsContext, transition::Transition, ModuleAssetContext, - }, -}; - -use super::with_chunking_context_scope_asset::WithChunkingContextScopeAsset; - -#[turbo_tasks::value(shared)] -pub struct NextSSRClientModuleTransition { - pub ssr_environment: Vc, - pub ssr_module_options_context: Vc, - pub ssr_resolve_options_context: Vc, -} - -#[turbo_tasks::value_impl] -impl Transition for NextSSRClientModuleTransition { - #[turbo_tasks::function] - fn process_compile_time_info( - &self, - _compile_time_info: Vc, - ) -> Vc { - self.ssr_environment - } - - #[turbo_tasks::function] - fn process_module_options_context( - &self, - _context: Vc, - ) -> Vc { - self.ssr_module_options_context - } - - #[turbo_tasks::function] - fn process_resolve_options_context( - &self, - _context: Vc, - ) -> Vc { - self.ssr_resolve_options_context - } - - #[turbo_tasks::function] - async fn process_module( - &self, - asset: Vc>, - _context: Vc, - ) -> Result>> { - Ok( - if let Some(placeable) = - Vc::try_resolve_sidecast::>(asset).await? - { - Vc::upcast( - WithChunkingContextScopeAsset { - asset: placeable, - layer: "ssr".to_string(), - } - .cell(), - ) - } else { - asset - }, - ) - } -} diff --git a/packages/next-swc/crates/next-core/src/next_client_component/with_chunking_context_scope_asset.rs b/packages/next-swc/crates/next-core/src/next_client_component/with_chunking_context_scope_asset.rs deleted file mode 100644 index 694db16ba4d1f..0000000000000 --- a/packages/next-swc/crates/next-core/src/next_client_component/with_chunking_context_scope_asset.rs +++ /dev/null @@ -1,65 +0,0 @@ -use turbo_tasks::Vc; -use turbopack_binding::turbopack::{ - core::{ - asset::{Asset, AssetContent}, - chunk::{ChunkableModule, ChunkingContext}, - ident::AssetIdent, - module::Module, - reference::ModuleReferences, - }, - turbopack::ecmascript::chunk::{EcmascriptChunkPlaceable, EcmascriptExports}, -}; - -#[turbo_tasks::function] -fn modifier() -> Vc { - Vc::cell("with chunking context scope".to_string()) -} - -#[turbo_tasks::value(shared)] -pub struct WithChunkingContextScopeAsset { - pub asset: Vc>, - pub layer: String, -} - -#[turbo_tasks::value_impl] -impl Module for WithChunkingContextScopeAsset { - #[turbo_tasks::function] - fn ident(&self) -> Vc { - self.asset.ident().with_modifier(modifier()) - } - - #[turbo_tasks::function] - fn references(&self) -> Vc { - self.asset.references() - } -} - -#[turbo_tasks::value_impl] -impl Asset for WithChunkingContextScopeAsset { - #[turbo_tasks::function] - fn content(&self) -> Vc { - self.asset.content() - } -} - -#[turbo_tasks::value_impl] -impl ChunkableModule for WithChunkingContextScopeAsset { - #[turbo_tasks::function] - fn as_chunk_item( - &self, - chunking_context: Vc>, - ) -> Vc> { - Vc::upcast(ChunkableModule::as_chunk_item( - self.asset, - chunking_context.with_layer(self.layer.clone()), - )) - } -} - -#[turbo_tasks::value_impl] -impl EcmascriptChunkPlaceable for WithChunkingContextScopeAsset { - #[turbo_tasks::function] - fn get_exports(&self) -> Vc { - self.asset.get_exports() - } -} diff --git a/packages/next-swc/crates/next-core/src/next_client_component/with_client_chunks.rs b/packages/next-swc/crates/next-core/src/next_client_component/with_client_chunks.rs deleted file mode 100644 index 94a3ffb96dd1d..0000000000000 --- a/packages/next-swc/crates/next-core/src/next_client_component/with_client_chunks.rs +++ /dev/null @@ -1,276 +0,0 @@ -use anyhow::{Context, Result}; -use indoc::formatdoc; -use turbo_tasks::{TryJoinIterExt, ValueToString, Vc}; -use turbopack_binding::{ - turbo::tasks_fs::FileSystemPath, - turbopack::{ - core::{ - asset::{Asset, AssetContent}, - chunk::{ - ChunkData, ChunkItem, ChunkItemExt, ChunkType, ChunkableModule, - ChunkableModuleReference, ChunkingContext, ChunkingContextExt, ChunksData, - }, - ident::AssetIdent, - module::Module, - output::{OutputAsset, OutputAssets}, - proxied_asset::ProxiedAsset, - reference::{ModuleReference, ModuleReferences, SingleOutputAssetReference}, - resolve::ModuleResolveResult, - }, - ecmascript::chunk::{EcmascriptChunkData, EcmascriptChunkType}, - turbopack::ecmascript::{ - chunk::{ - EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable, - EcmascriptChunkingContext, EcmascriptExports, - }, - utils::StringifyJs, - }, - }, -}; - -#[turbo_tasks::function] -fn modifier() -> Vc { - Vc::cell("client chunks".to_string()) -} - -#[turbo_tasks::value(shared)] -pub struct WithClientChunksAsset { - pub asset: Vc>, - pub server_root: Vc, -} - -#[turbo_tasks::value_impl] -impl Module for WithClientChunksAsset { - #[turbo_tasks::function] - fn ident(&self) -> Vc { - self.asset.ident().with_modifier(modifier()) - } - - #[turbo_tasks::function] - fn references(&self) -> Vc { - Vc::cell(vec![Vc::upcast( - WithClientChunksAssetReference { - asset: Vc::upcast(self.asset), - } - .cell(), - )]) - } -} - -#[turbo_tasks::value_impl] -impl Asset for WithClientChunksAsset { - #[turbo_tasks::function] - fn content(&self) -> Vc { - unimplemented!() - } -} - -#[turbo_tasks::value_impl] -impl ChunkableModule for WithClientChunksAsset { - #[turbo_tasks::function] - async fn as_chunk_item( - self: Vc, - chunking_context: Vc>, - ) -> Result>> { - let context = Vc::try_resolve_sidecast::>( - chunking_context.with_layer("rsc".to_string()), - ) - .await? - .context( - "ChunkingContext::with_layer should not return a different kind of chunking context", - )?; - Ok(Vc::upcast( - WithClientChunksChunkItem { - context, - inner: self, - } - .cell(), - )) - } -} - -#[turbo_tasks::value_impl] -impl EcmascriptChunkPlaceable for WithClientChunksAsset { - #[turbo_tasks::function] - fn get_exports(&self) -> Vc { - // TODO This should be EsmExports - EcmascriptExports::Value.cell() - } -} - -#[turbo_tasks::value] -struct WithClientChunksChunkItem { - context: Vc>, - inner: Vc, -} - -#[turbo_tasks::value_impl] -impl WithClientChunksChunkItem { - #[turbo_tasks::function] - async fn chunks(self: Vc) -> Result> { - let this = self.await?; - let inner = this.inner.await?; - Ok(this.context.root_chunk_group(Vc::upcast(inner.asset))) - } - - #[turbo_tasks::function] - async fn client_chunks(self: Vc) -> Result> { - let this = self.await?; - let inner = this.inner.await?; - let chunks = self.chunks(); - let output_root = this.context.output_root().await?; - - let mut client_chunks = Vec::new(); - for &chunk in &*chunks.await? { - let extension = chunk.ident().path().extension().await?; - // Only expose CSS chunks as client chunks. - if &*extension == "css" { - if let Some(path) = output_root.get_path_to(&*chunk.ident().path().await?) { - client_chunks.push(Vc::upcast(ProxiedAsset::new( - Vc::upcast(chunk), - inner.server_root.join(path.to_string()), - ))); - } - } - } - - Ok(Vc::cell(client_chunks)) - } - - #[turbo_tasks::function] - async fn chunks_data(self: Vc) -> Result> { - let this = self.await?; - let inner = this.inner.await?; - Ok(ChunkData::from_assets( - inner.server_root, - self.client_chunks(), - )) - } -} - -#[turbo_tasks::value_impl] -impl EcmascriptChunkItem for WithClientChunksChunkItem { - #[turbo_tasks::function] - fn chunking_context(&self) -> Vc> { - self.context - } - - #[turbo_tasks::function] - async fn content(self: Vc) -> Result> { - let this = self.await?; - let inner = this.inner.await?; - - let chunks_data = self.chunks_data().await?; - let chunks_data = chunks_data.iter().try_join().await?; - let chunks_data: Vec<_> = chunks_data - .iter() - .map(|chunk_data| EcmascriptChunkData::new(chunk_data)) - .collect(); - - let module_id = inner - .asset - .as_chunk_item(Vc::upcast(this.context)) - .id() - .await?; - Ok(EcmascriptChunkItemContent { - inner_code: formatdoc!( - // We store the chunks in a binding, otherwise a new array would be created every - // time the export binding is read. - r#" - __turbopack_esm__({{ - default: () => __turbopack_import__({}), - chunks: () => chunks, - }}); - const chunks = {:#}; - "#, - StringifyJs(&module_id), - StringifyJs(&chunks_data), - ) - .into(), - ..Default::default() - } - .cell()) - } -} - -#[turbo_tasks::value_impl] -impl ChunkItem for WithClientChunksChunkItem { - #[turbo_tasks::function] - fn asset_ident(&self) -> Vc { - self.inner.ident() - } - - #[turbo_tasks::function] - async fn references(self: Vc) -> Result> { - let this = self.await?; - let inner = this.inner.await?; - let mut references = Vec::new(); - references.push(Vc::upcast( - WithClientChunksAssetReference { - asset: Vc::upcast(inner.asset), - } - .cell(), - )); - let client_chunks = self.client_chunks(); - let client_chunks = client_chunks.await?; - let client_chunk = Vc::cell("client chunk".to_string()); - for &chunk in client_chunks.iter() { - references.push(Vc::upcast(SingleOutputAssetReference::new( - chunk, - client_chunk, - ))); - } - let chunk_data_key = Vc::cell("chunk data".to_string()); - for chunk_data in &*self.chunks_data().await? { - references.extend(chunk_data.references().await?.iter().map(|&output_asset| { - Vc::upcast(SingleOutputAssetReference::new( - output_asset, - chunk_data_key, - )) - })); - } - Ok(Vc::cell(references)) - } - - #[turbo_tasks::function] - async fn chunking_context(&self) -> Vc> { - Vc::upcast(self.context) - } - - #[turbo_tasks::function] - fn ty(&self) -> Vc> { - Vc::upcast(Vc::::default()) - } - - #[turbo_tasks::function] - fn module(&self) -> Vc> { - Vc::upcast(self.inner) - } -} - -#[turbo_tasks::value] -struct WithClientChunksAssetReference { - asset: Vc>, -} - -#[turbo_tasks::value_impl] -impl ValueToString for WithClientChunksAssetReference { - #[turbo_tasks::function] - async fn to_string(&self) -> Result> { - Ok(Vc::cell(format!( - "local asset {}", - self.asset.ident().to_string().await? - ))) - } -} - -#[turbo_tasks::value_impl] -impl ModuleReference for WithClientChunksAssetReference { - #[turbo_tasks::function] - fn resolve_reference(&self) -> Vc { - ModuleResolveResult::module(self.asset).cell() - } -} - -#[turbo_tasks::value_impl] -impl ChunkableModuleReference for WithClientChunksAssetReference {} diff --git a/packages/next-swc/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_transition.rs b/packages/next-swc/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_transition.rs index 0425f183d0b0e..32a72b0fb609f 100644 --- a/packages/next-swc/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_transition.rs +++ b/packages/next-swc/crates/next-core/src/next_client_reference/ecmascript_client_reference/ecmascript_client_reference_transition.rs @@ -39,13 +39,22 @@ impl NextEcmascriptClientReferenceTransition { #[turbo_tasks::value_impl] impl Transition for NextEcmascriptClientReferenceTransition { + #[turbo_tasks::function] + fn process_layer(self: Vc, layer: Vc) -> Vc { + layer + } + #[turbo_tasks::function] async fn process( - &self, + self: Vc, source: Vc>, context: Vc, _reference_type: Value, ) -> Result>> { + let context = self.process_context(context); + + let this = self.await?; + let ident = source.ident().await?; let ident_path = ident.path.await?; let client_source = if ident_path.path.contains("next/dist/esm/") { @@ -57,7 +66,7 @@ impl Transition for NextEcmascriptClientReferenceTransition { } else { source }; - let client_module = self.client_transition.process( + let client_module = this.client_transition.process( client_source, context, Value::new(ReferenceType::Entry( @@ -65,7 +74,7 @@ impl Transition for NextEcmascriptClientReferenceTransition { )), ); - let ssr_module = self.ssr_transition.process( + let ssr_module = this.ssr_transition.process( source, context, Value::new(ReferenceType::Entry( @@ -93,6 +102,7 @@ impl Transition for NextEcmascriptClientReferenceTransition { context.compile_time_info, context.module_options_context, context.resolve_options_context, + context.layer, ); Ok(Vc::upcast(EcmascriptClientReferenceProxyModule::new( diff --git a/packages/next-swc/crates/next-core/src/next_config.rs b/packages/next-swc/crates/next-core/src/next_config.rs index cf8787db59dc7..182a6da209b67 100644 --- a/packages/next-swc/crates/next-core/src/next_config.rs +++ b/packages/next-swc/crates/next-core/src/next_config.rs @@ -9,7 +9,6 @@ use turbopack_binding::{ turbopack::{ core::{ changed::any_content_changed_of_module, - chunk::ChunkingContext, context::AssetContext, file_source::FileSource, ident::AssetIdent, @@ -803,7 +802,12 @@ async fn load_next_config_and_custom_routes_internal( import_map.insert_exact_alias("styled-jsx", ImportMapping::External(None).into()); import_map.insert_wildcard_alias("styled-jsx/", ImportMapping::External(None).into()); - let context = node_evaluate_asset_context(execution_context, Some(import_map.cell()), None); + let context = node_evaluate_asset_context( + execution_context, + Some(import_map.cell()), + None, + "next_config".to_string(), + ); let config_asset = config_file.map(FileSource::new); let config_changed = config_asset.map_or_else(Completion::immutable, |config_asset| { @@ -825,7 +829,7 @@ async fn load_next_config_and_custom_routes_internal( env, config_asset.map_or_else(|| AssetIdent::from_path(project_path), |c| c.ident()), context, - chunking_context.with_layer("next_config".to_string()), + chunking_context, None, vec![], config_changed, diff --git a/packages/next-swc/crates/next-core/src/next_dynamic/dynamic_transition.rs b/packages/next-swc/crates/next-core/src/next_dynamic/dynamic_transition.rs index 82c4d974c2dde..3f82dfe025375 100644 --- a/packages/next-swc/crates/next-core/src/next_dynamic/dynamic_transition.rs +++ b/packages/next-swc/crates/next-core/src/next_dynamic/dynamic_transition.rs @@ -28,15 +28,24 @@ impl NextDynamicTransition { #[turbo_tasks::value_impl] impl Transition for NextDynamicTransition { + #[turbo_tasks::function] + fn process_layer(self: Vc, layer: Vc) -> Vc { + layer + } + #[turbo_tasks::function] async fn process( - &self, + self: Vc, source: Vc>, context: Vc, _reference_type: Value, ) -> Result>> { + let context = self.process_context(context); + + let this = self.await?; + let client_module = - self.client_transition + this.client_transition .process(source, context, Value::new(ReferenceType::Undefined)); Ok(Vc::upcast(NextDynamicEntryModule::new(client_module))) diff --git a/packages/next-swc/crates/next-core/src/next_edge/mod.rs b/packages/next-swc/crates/next-core/src/next_edge/mod.rs index d0e316ae0af81..6c72552f20d87 100644 --- a/packages/next-swc/crates/next-core/src/next_edge/mod.rs +++ b/packages/next-swc/crates/next-core/src/next_edge/mod.rs @@ -1,5 +1,3 @@ pub mod context; pub mod entry; -pub mod page_transition; pub mod route_regex; -pub mod route_transition; diff --git a/packages/next-swc/crates/next-core/src/next_edge/page_transition.rs b/packages/next-swc/crates/next-core/src/next_edge/page_transition.rs deleted file mode 100644 index e41fdcda1f95c..0000000000000 --- a/packages/next-swc/crates/next-core/src/next_edge/page_transition.rs +++ /dev/null @@ -1,100 +0,0 @@ -use anyhow::{bail, Result}; -use indexmap::indexmap; -use turbo_tasks::{Value, Vc}; -use turbopack_binding::{ - turbo::tasks_fs::FileSystemPath, - turbopack::{ - core::{ - chunk::{ChunkableModule, ChunkingContext}, - compile_time_info::CompileTimeInfo, - context::AssetContext, - file_source::FileSource, - module::Module, - reference_type::{EcmaScriptModulesReferenceSubType, ReferenceType}, - source::Source, - }, - ecmascript::chunk_group_files_asset::ChunkGroupFilesAsset, - turbopack::{ - module_options::ModuleOptionsContext, resolve_options_context::ResolveOptionsContext, - transition::Transition, ModuleAssetContext, - }, - }, -}; - -use crate::embed_js::next_js_file_path; - -/// Transition into edge environment to render an app directory page. -/// -/// It changes the environment to the provided edge environment, and wraps the -/// process asset with the provided bootstrap_asset returning the chunks of all -/// that for running them in the edge sandbox. -#[turbo_tasks::value(shared)] -pub struct NextEdgePageTransition { - pub edge_compile_time_info: Vc, - pub edge_chunking_context: Vc>, - pub edge_module_options_context: Option>, - pub edge_resolve_options_context: Vc, - pub output_path: Vc, - pub bootstrap_asset: Vc>, -} - -#[turbo_tasks::value_impl] -impl Transition for NextEdgePageTransition { - #[turbo_tasks::function] - fn process_compile_time_info( - &self, - _compile_time_info: Vc, - ) -> Vc { - self.edge_compile_time_info - } - - #[turbo_tasks::function] - fn process_module_options_context( - &self, - context: Vc, - ) -> Vc { - self.edge_module_options_context.unwrap_or(context) - } - - #[turbo_tasks::function] - fn process_resolve_options_context( - &self, - _context: Vc, - ) -> Vc { - self.edge_resolve_options_context - } - - #[turbo_tasks::function] - async fn process_module( - &self, - asset: Vc>, - context: Vc, - ) -> Result>> { - let module = context.process( - self.bootstrap_asset, - Value::new(ReferenceType::Internal(Vc::cell(indexmap! { - "APP_ENTRY".to_string() => asset, - "APP_BOOTSTRAP".to_string() => context.with_transition("next-client".to_string()).process( - Vc::upcast(FileSource::new(next_js_file_path("entry/app/hydrate.tsx".to_string()))), - Value::new(ReferenceType::EcmaScriptModules( - EcmaScriptModulesReferenceSubType::Undefined, - )), - ), - }))), - ); - - let Some(module) = Vc::try_resolve_sidecast::>(module).await? - else { - bail!("Internal module is not chunkable"); - }; - - let asset = ChunkGroupFilesAsset { - module, - client_root: self.output_path, - chunking_context: self.edge_chunking_context, - runtime_entries: None, - }; - - Ok(Vc::upcast(asset.cell())) - } -} diff --git a/packages/next-swc/crates/next-core/src/next_edge/route_transition.rs b/packages/next-swc/crates/next-core/src/next_edge/route_transition.rs deleted file mode 100644 index 05326de255203..0000000000000 --- a/packages/next-swc/crates/next-core/src/next_edge/route_transition.rs +++ /dev/null @@ -1,86 +0,0 @@ -use anyhow::Result; -use indexmap::indexmap; -use turbo_tasks::Vc; -use turbopack_binding::{ - turbo::tasks_fs::FileSystemPath, - turbopack::{ - core::{ - chunk::ChunkingContext, compile_time_info::CompileTimeInfo, module::Module, - source::Source, - }, - ecmascript::chunk_group_files_asset::ChunkGroupFilesAsset, - turbopack::{ - module_options::ModuleOptionsContext, resolve_options_context::ResolveOptionsContext, - transition::Transition, ModuleAssetContext, - }, - }, -}; - -use crate::bootstrap::route_bootstrap; - -#[turbo_tasks::value(shared)] -pub struct NextEdgeRouteTransition { - pub edge_compile_time_info: Vc, - pub edge_chunking_context: Vc>, - pub edge_module_options_context: Option>, - pub edge_resolve_options_context: Vc, - pub output_path: Vc, - pub base_path: Vc, - pub bootstrap_asset: Vc>, - pub entry_name: String, -} - -#[turbo_tasks::value_impl] -impl Transition for NextEdgeRouteTransition { - #[turbo_tasks::function] - fn process_compile_time_info( - &self, - _compile_time_info: Vc, - ) -> Vc { - self.edge_compile_time_info - } - - #[turbo_tasks::function] - fn process_module_options_context( - &self, - context: Vc, - ) -> Vc { - self.edge_module_options_context.unwrap_or(context) - } - - #[turbo_tasks::function] - fn process_resolve_options_context( - &self, - _context: Vc, - ) -> Vc { - self.edge_resolve_options_context - } - - #[turbo_tasks::function] - async fn process_module( - self: Vc, - asset: Vc>, - context: Vc, - ) -> Result>> { - let new_context = self.process_context(context); - let this = self.await?; - let new_asset = route_bootstrap( - asset, - Vc::upcast(new_context), - this.base_path, - this.bootstrap_asset, - Vc::cell(indexmap! { - "NAME".to_string() => this.entry_name.clone(), - }), - ); - - let asset = ChunkGroupFilesAsset { - module: Vc::upcast(new_asset), - client_root: this.output_path, - chunking_context: this.edge_chunking_context, - runtime_entries: None, - }; - - Ok(Vc::upcast(asset.cell())) - } -} diff --git a/packages/next-swc/crates/next-core/src/next_font/google/mod.rs b/packages/next-swc/crates/next-core/src/next_font/google/mod.rs index b264ce3e192cd..094833ceca549 100644 --- a/packages/next-swc/crates/next-core/src/next_font/google/mod.rs +++ b/packages/next-swc/crates/next-core/src/next_font/google/mod.rs @@ -443,7 +443,8 @@ async fn get_mock_stylesheet( project_path: _, chunking_context, } = *execution_context.await?; - let context = node_evaluate_asset_context(execution_context, None, None); + let context = + node_evaluate_asset_context(execution_context, None, None, "next_font".to_string()); let loader_path = mock_fs.root().join("loader.js".to_string()); let mocked_response_asset = context.process( Vc::upcast(VirtualSource::new( diff --git a/packages/next-swc/crates/next-core/src/next_server/mod.rs b/packages/next-swc/crates/next-core/src/next_server/mod.rs index 982e11165f5dc..fbc8be4401df5 100644 --- a/packages/next-swc/crates/next-core/src/next_server/mod.rs +++ b/packages/next-swc/crates/next-core/src/next_server/mod.rs @@ -1,6 +1,5 @@ pub(crate) mod context; pub(crate) mod resolve; -pub mod route_transition; pub(crate) mod transforms; pub use context::{ diff --git a/packages/next-swc/crates/next-core/src/next_server/route_transition.rs b/packages/next-swc/crates/next-core/src/next_server/route_transition.rs deleted file mode 100644 index 1c1de254b5b4a..0000000000000 --- a/packages/next-swc/crates/next-core/src/next_server/route_transition.rs +++ /dev/null @@ -1,30 +0,0 @@ -use turbo_tasks::Vc; -use turbopack_binding::turbopack::{ - core::compile_time_info::CompileTimeInfo, - turbopack::{resolve_options_context::ResolveOptionsContext, transition::Transition}, -}; - -#[turbo_tasks::value(shared)] -pub struct NextRouteTransition { - pub server_compile_time_info: Vc, - pub server_resolve_options_context: Vc, -} - -#[turbo_tasks::value_impl] -impl Transition for NextRouteTransition { - #[turbo_tasks::function] - fn process_compile_time_info( - &self, - _compile_time_info: Vc, - ) -> Vc { - self.server_compile_time_info - } - - #[turbo_tasks::function] - fn process_resolve_options_context( - &self, - _context: Vc, - ) -> Vc { - self.server_resolve_options_context - } -} diff --git a/packages/next-swc/crates/next-core/src/next_server_component/server_component_transition.rs b/packages/next-swc/crates/next-core/src/next_server_component/server_component_transition.rs index f19dd0a0eeeb1..18df3fb6d81d8 100644 --- a/packages/next-swc/crates/next-core/src/next_server_component/server_component_transition.rs +++ b/packages/next-swc/crates/next-core/src/next_server_component/server_component_transition.rs @@ -28,6 +28,11 @@ impl NextServerComponentTransition { #[turbo_tasks::value_impl] impl Transition for NextServerComponentTransition { + #[turbo_tasks::function] + fn process_layer(self: Vc, layer: Vc) -> Vc { + layer + } + #[turbo_tasks::function] async fn process_module( self: Vc, diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index 8d2e8ad92b6fa..1b425726f7b7e 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "private": true, "scripts": { "clean": "node ../../scripts/rm.mjs native", diff --git a/packages/next/package.json b/packages/next/package.json index 84d0b7f30dc70..db7323ff08e4f 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -92,7 +92,7 @@ ] }, "dependencies": { - "@next/env": "13.5.7-canary.12", + "@next/env": "13.5.7-canary.13", "@swc/helpers": "0.5.2", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", @@ -146,11 +146,11 @@ "@mswjs/interceptors": "0.23.0", "@napi-rs/cli": "2.16.2", "@napi-rs/triples": "1.1.0", - "@next/polyfill-module": "13.5.7-canary.12", - "@next/polyfill-nomodule": "13.5.7-canary.12", - "@next/react-dev-overlay": "13.5.7-canary.12", - "@next/react-refresh-utils": "13.5.7-canary.12", - "@next/swc": "13.5.7-canary.12", + "@next/polyfill-module": "13.5.7-canary.13", + "@next/polyfill-nomodule": "13.5.7-canary.13", + "@next/react-dev-overlay": "13.5.7-canary.13", + "@next/react-refresh-utils": "13.5.7-canary.13", + "@next/swc": "13.5.7-canary.13", "@opentelemetry/api": "1.4.1", "@playwright/test": "^1.35.1", "@taskr/clear": "1.1.0", @@ -193,7 +193,7 @@ "@types/ws": "8.2.0", "@vercel/ncc": "0.34.0", "@vercel/nft": "0.22.6", - "@vercel/turbopack-ecmascript-runtime": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.3", + "@vercel/turbopack-ecmascript-runtime": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.4", "acorn": "8.5.0", "amphtml-validator": "1.0.35", "anser": "1.4.9", diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index 0f76524b4cb60..60b05a35b782a 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -519,11 +519,12 @@ export interface HmrIdentifiers { identifiers: string[] } -export interface StackFrame { +interface TurbopackStackFrame { + column: number | null file: string - methodName: string | null + isServer: boolean line: number - column: number | null + methodName: string | null } export interface UpdateInfo { @@ -548,7 +549,9 @@ export interface Project { TurbopackResult > getSourceForAsset(filePath: string): Promise - traceSource(stackFrame: StackFrame): Promise + traceSource( + stackFrame: TurbopackStackFrame + ): Promise updateInfoSubscribe(): AsyncIterableIterator> } @@ -925,7 +928,9 @@ function bindingToApi(binding: any, _wasm: boolean) { return subscription } - traceSource(stackFrame: StackFrame): Promise { + traceSource( + stackFrame: TurbopackStackFrame + ): Promise { return binding.projectTraceSource(this._nativeProject, stackFrame) } diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts index c3c5a43506f3d..15f5b5b7b13d0 100644 --- a/packages/next/src/build/webpack-config.ts +++ b/packages/next/src/build/webpack-config.ts @@ -17,6 +17,7 @@ import { RSC_ACTION_CLIENT_WRAPPER_ALIAS, RSC_ACTION_VALIDATE_ALIAS, WEBPACK_RESOURCE_QUERIES, + RSC_ACTION_ENCRYPTION_ALIAS, } from '../lib/constants' import type { WebpackLayerName } from '../lib/constants' import { isWebpackDefaultLayer, isWebpackServerLayer } from './utils' @@ -481,7 +482,6 @@ export default async function getBaseWebpackConfig( rewrites.fallback.length > 0 const hasAppDir = !!appDir - const hasServerComponents = hasAppDir const disableOptimizedLoading = true const enableTypedRoutes = !!config.experimental.typedRoutes && hasAppDir const bundledReactChannel = needsExperimentalReact(config) @@ -534,7 +534,7 @@ export default async function getBaseWebpackConfig( pagesDir, cwd: dir, development: dev, - hasServerComponents, + hasServerComponents: hasAppDir, hasReactRefresh: dev && isClient, hasJsxRuntime: true, }, @@ -586,7 +586,7 @@ export default async function getBaseWebpackConfig( : getBabelLoader(), } - const swcLoaderForServerLayer = hasServerComponents + const swcLoaderForServerLayer = hasAppDir ? useSWCLoader ? [getSwcLoader({ isServerLayer: true, bundleTarget: 'server' })] : // When using Babel, we will have to add the SWC loader @@ -632,11 +632,11 @@ export default async function getBaseWebpackConfig( // in the client layer. loader: 'next-flight-client-module-loader', }, - ...(hasServerComponents + ...(hasAppDir ? useSWCLoader ? [ getSwcLoader({ - hasServerComponents, + hasServerComponents: hasAppDir, isServerLayer: false, bundleTarget: 'client', }), @@ -659,7 +659,7 @@ export default async function getBaseWebpackConfig( // have RSC transpiler enabled, so syntax checks such as invalid imports won't // be performed. const loaderForAPIRoutes = - hasServerComponents && useSWCLoader + hasAppDir && useSWCLoader ? getSwcLoader({ isServerLayer: false, bundleTarget: 'default', @@ -902,6 +902,9 @@ export default async function getBaseWebpackConfig( [RSC_ACTION_PROXY_ALIAS]: 'next/dist/build/webpack/loaders/next-flight-loader/action-proxy', + [RSC_ACTION_ENCRYPTION_ALIAS]: + 'next/dist/server/app-render/action-encryption', + ...(isClient || isEdgeServer ? { [clientResolveRewrites]: hasRewrites @@ -1392,7 +1395,6 @@ export default async function getBaseWebpackConfig( 'next-flight-client-entry-loader', 'next-flight-action-entry-loader', 'next-flight-client-module-loader', - 'noop-loader', 'empty-loader', 'next-middleware-loader', 'next-edge-function-loader', @@ -1650,14 +1652,14 @@ export default async function getBaseWebpackConfig( }, ] : []), - ...(hasServerComponents + ...(hasAppDir ? [ { // Alias react-dom for ReactDOM.preload usage. // Alias react for switching between default set and share subset. oneOf: [ { - exclude: [asyncStoragesRegex], + exclude: asyncStoragesRegex, issuerLayer: isWebpackServerLayer, test: { // Resolve it if it is a source code file, and it has NOT been @@ -1728,12 +1730,12 @@ export default async function getBaseWebpackConfig( issuerLayer: WEBPACK_LAYERS.middleware, use: swcLoaderForMiddlewareLayer, }, - ...(hasServerComponents + ...(hasAppDir ? [ { test: codeCondition.test, issuerLayer: isWebpackServerLayer, - exclude: [asyncStoragesRegex], + exclude: asyncStoragesRegex, use: swcLoaderForServerLayer, }, { @@ -1749,7 +1751,7 @@ export default async function getBaseWebpackConfig( WEBPACK_LAYERS.appPagesBrowser, WEBPACK_LAYERS.serverSideRendering, ], - exclude: [codeCondition.exclude], + exclude: codeCondition.exclude, use: swcLoaderForClientLayer, resolve: { mainFields: getMainField('app', compilerType), @@ -1903,7 +1905,7 @@ export default async function getBaseWebpackConfig( test: /[\\/]next[\\/]dist[\\/](esm[\\/])?server[\\/]og[\\/]image-response\.js/, sideEffects: false, }, - ].filter(Boolean), + ], }, plugins: [ isNodeServer && @@ -2002,7 +2004,7 @@ export default async function getBaseWebpackConfig( } = require('./webpack/plugins/nextjs-require-cache-hot-reloader') const devPlugins = [ new NextJsRequireCacheHotReloader({ - hasServerComponents, + hasServerComponents: hasAppDir, }), ] @@ -2069,7 +2071,7 @@ export default async function getBaseWebpackConfig( }, }), hasAppDir && isClient && new AppBuildManifestPlugin({ dev }), - hasServerComponents && + hasAppDir && (isClient ? new ClientReferenceManifestPlugin({ dev, @@ -2235,7 +2237,7 @@ export default async function getBaseWebpackConfig( // For Server Components, it's necessary to have provided exports collected // to generate the correct flight manifest. - if (!hasServerComponents) { + if (!hasAppDir) { webpack5Config.optimization.providedExports = false } webpack5Config.optimization.usedExports = false diff --git a/packages/next/src/build/webpack/loaders/next-app-loader.ts b/packages/next/src/build/webpack/loaders/next-app-loader.ts index 73c6db1fdcd20..8731b5177096f 100644 --- a/packages/next/src/build/webpack/loaders/next-app-loader.ts +++ b/packages/next/src/build/webpack/loaders/next-app-loader.ts @@ -6,7 +6,7 @@ import path from 'path' import { stringify } from 'querystring' import { bold } from '../../../lib/picocolors' import { getModuleBuildInfo } from './get-module-build-info' -import { verifyRootLayout } from '../../../lib/verifyRootLayout' +import { verifyRootLayout } from '../../../lib/verify-root-layout' import * as Log from '../../output/log' import { APP_DIR_ALIAS, WEBPACK_RESOURCE_QUERIES } from '../../../lib/constants' import { diff --git a/packages/next/src/build/webpack/loaders/next-flight-loader/action-proxy.ts b/packages/next/src/build/webpack/loaders/next-flight-loader/action-proxy.ts index 8971228745b1f..0fe5ef99f21fb 100644 --- a/packages/next/src/build/webpack/loaders/next-flight-loader/action-proxy.ts +++ b/packages/next/src/build/webpack/loaders/next-flight-loader/action-proxy.ts @@ -2,7 +2,7 @@ const SERVER_REFERENCE_TAG = Symbol.for('react.server.reference') export function createActionProxy( id: string, - bound: null | any[], + boundArgsFromClosure: null | any[], action: any, originalAction?: any ) { @@ -40,7 +40,7 @@ export function createActionProxy( value: id, }, $$bound: { - value: bound, + value: boundArgsFromClosure, }, bind: { value: bindImpl, diff --git a/packages/next/src/build/webpack/loaders/noop-loader.ts b/packages/next/src/build/webpack/loaders/noop-loader.ts deleted file mode 100644 index 0d24991b45520..0000000000000 --- a/packages/next/src/build/webpack/loaders/noop-loader.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { webpack } from 'next/dist/compiled/webpack/webpack' - -const NoopLoader: webpack.LoaderDefinitionFunction = (source) => source -export default NoopLoader diff --git a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts index 814db070c7465..42f61cd32fa75 100644 --- a/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts @@ -23,8 +23,8 @@ import { SERVER_REFERENCE_MANIFEST, } from '../../../shared/lib/constants' import { - generateActionId, getActions, + generateActionId, isClientComponentEntryModule, isCSSMod, regexCSS, @@ -33,6 +33,8 @@ import { traverseModules, forEachEntryModule } from '../utils' import { normalizePathSep } from '../../../shared/lib/page-path/normalize-path-sep' import { getProxiedPluginState } from '../../build-context' import type { SizeLimit } from '../../../../types' +import semver from 'next/dist/compiled/semver' +import { generateRandomActionKeyRaw } from '../../../server/app-render/action-encryption-utils' interface Options { dev: boolean @@ -43,20 +45,25 @@ interface Options { const PLUGIN_NAME = 'FlightClientEntryPlugin' -export type ActionManifest = { - [key in 'node' | 'edge']: { - [actionId: string]: { - workers: { - [name: string]: string | number - } - // Record which layer the action is in (rsc or sc_action), in the specific entry. - layer: { - [name: string]: string - } +type Actions = { + [actionId: string]: { + workers: { + [name: string]: string | number + } + // Record which layer the action is in (rsc or sc_action), in the specific entry. + layer: { + [name: string]: string } } } +export type ActionManifest = { + // Assign a unique encryption key during production build. + encryptionKey: string + node: Actions + edge: Actions +} + const pluginState = getProxiedPluginState({ // A map to track "action" -> "list of bundles". serverActions: {} as ActionManifest['node'], @@ -233,7 +240,7 @@ export class FlightClientEntryPlugin { }) compiler.hooks.make.tap(PLUGIN_NAME, (compilation) => { - compilation.hooks.processAssets.tap( + compilation.hooks.processAssets.tapPromise( { name: PLUGIN_NAME, stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH, @@ -784,6 +791,18 @@ export class FlightClientEntryPlugin { fromClient?: boolean }) { const actionsArray = Array.from(actions.entries()) + + // Node < 18.11 does not have sufficient support for FormData + if (actionsArray.length > 0 && semver.lt(process.version, '18.11.0')) { + compilation.errors.push( + new compilation.compiler.webpack.WebpackError( + 'Your version of Node does not support server actions. Please upgrade to Node 18.11 or higher.' + ) + ) + + return Promise.resolve() + } + const actionLoader = `next-flight-action-entry-loader?${stringify({ actions: JSON.stringify(actionsArray), __client_imported__: fromClient, @@ -856,7 +875,7 @@ export class FlightClientEntryPlugin { }) } - createActionAssets( + async createActionAssets( compilation: webpack.Compilation, assets: webpack.Compilation['assets'] ) { @@ -915,6 +934,9 @@ export class FlightClientEntryPlugin { { node: serverActions, edge: edgeServerActions, + + // Assign encryption + encryptionKey: await generateRandomActionKeyRaw(this.dev), }, null, this.dev ? 2 : undefined diff --git a/packages/next/src/cli/next-dev.ts b/packages/next/src/cli/next-dev.ts index 0bce072de3da2..6ae976d47f82c 100644 --- a/packages/next/src/cli/next-dev.ts +++ b/packages/next/src/cli/next-dev.ts @@ -2,7 +2,14 @@ import '../server/lib/cpu-profile' import type { StartServerOptions } from '../server/lib/start-server' -import { RESTART_EXIT_CODE, getPort, printAndExit } from '../server/lib/utils' +import { + RESTART_EXIT_CODE, + checkNodeDebugType, + getDebugPort, + getNodeOptionsWithoutInspect, + getPort, + printAndExit, +} from '../server/lib/utils' import * as Log from '../build/output/log' import type { CliCommand } from '../lib/commands' import { getProjectDir } from '../lib/get-project-dir' @@ -223,6 +230,15 @@ const nextDev: CliCommand = async (args) => { let resolved = false const defaultEnv = (initialEnv || process.env) as typeof process.env + let NODE_OPTIONS = getNodeOptionsWithoutInspect() + let nodeDebugType = checkNodeDebugType() + + if (nodeDebugType) { + NODE_OPTIONS = `${NODE_OPTIONS} --${nodeDebugType}=${ + getDebugPort() + 1 + }` + } + child = fork(startServerPath, { stdio: 'inherit', env: { @@ -232,6 +248,7 @@ const nextDev: CliCommand = async (args) => { NODE_EXTRA_CA_CERTS: options.selfSignedCertificate ? options.selfSignedCertificate.rootCA : defaultEnv.NODE_EXTRA_CA_CERTS, + NODE_OPTIONS, }, }) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/parseStack.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/parseStack.ts index fbd509339945c..1a0767318cb55 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/parseStack.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/parseStack.ts @@ -1,7 +1,7 @@ import { parse } from 'next/dist/compiled/stacktrace-parser' import type { StackFrame } from 'next/dist/compiled/stacktrace-parser' -const regexNextStatic = /\/_next(\/static\/.+)/g +const regexNextStatic = /\/_next(\/static\/.+)/ export function parseStack(stack: string): StackFrame[] { const frames = parse(stack) diff --git a/packages/next/src/export/worker.ts b/packages/next/src/export/worker.ts index daab596e8e6ec..cfed198f0bcbb 100644 --- a/packages/next/src/export/worker.ts +++ b/packages/next/src/export/worker.ts @@ -172,7 +172,7 @@ async function exportPageImpl( dl.defaultLocale === locale || dl.locales?.includes(locale || '') ) ) { - addRequestMeta(req, '__nextIsLocaleDomain', true) + addRequestMeta(req, 'isLocaleDomain', true) } envConfig.setConfig({ diff --git a/packages/next/src/lib/constants.ts b/packages/next/src/lib/constants.ts index 2a605586b817a..efa82928cd0da 100644 --- a/packages/next/src/lib/constants.ts +++ b/packages/next/src/lib/constants.ts @@ -35,6 +35,7 @@ export const APP_DIR_ALIAS = 'private-next-app-dir' export const RSC_MOD_REF_PROXY_ALIAS = 'private-next-rsc-mod-ref-proxy' export const RSC_ACTION_VALIDATE_ALIAS = 'private-next-rsc-action-validate' export const RSC_ACTION_PROXY_ALIAS = 'private-next-rsc-action-proxy' +export const RSC_ACTION_ENCRYPTION_ALIAS = 'private-next-rsc-action-encryption' export const RSC_ACTION_CLIENT_WRAPPER_ALIAS = 'private-next-rsc-action-client-wrapper' diff --git a/packages/next/src/lib/turbopack-warning.ts b/packages/next/src/lib/turbopack-warning.ts index 5b0e52625b6bc..42c7301bbbf95 100644 --- a/packages/next/src/lib/turbopack-warning.ts +++ b/packages/next/src/lib/turbopack-warning.ts @@ -45,6 +45,9 @@ const supportedTurbopackNextConfigOptions = [ 'webpack', 'onDemandEntries', 'experimental.cpus', + 'serverRuntimeConfig', + 'publicRuntimeConfig', + 'exportPathMap', // Experimental options that affect compilation 'experimental.swcPlugins', @@ -64,8 +67,6 @@ const supportedTurbopackNextConfigOptions = [ 'experimental.deploymentId', // Experimental options that don't affect compilation - 'serverRuntimeConfig', - 'publicRuntimeConfig', 'experimental.proxyTimeout', 'experimental.caseSensitiveRoutes', 'experimental.workerThreads', diff --git a/packages/next/src/lib/verifyRootLayout.ts b/packages/next/src/lib/verify-root-layout.ts similarity index 92% rename from packages/next/src/lib/verifyRootLayout.ts rename to packages/next/src/lib/verify-root-layout.ts index a6a473bae4526..ac0f2b5738484 100644 --- a/packages/next/src/lib/verifyRootLayout.ts +++ b/packages/next/src/lib/verify-root-layout.ts @@ -1,6 +1,7 @@ import path from 'path' +import * as Log from '../build/output/log' import { promises as fs } from 'fs' -import { bold, green } from './picocolors' +import { bold } from './picocolors' import { APP_DIR_ALIAS } from './constants' const globOrig = @@ -123,14 +124,12 @@ export async function verifyRootLayout({ ) await fs.writeFile(rootLayoutPath, getRootLayout(hasTsConfig)) - console.log( - green( - `\nYour page ${bold( - `app/${normalizedPagePath}` - )} did not have a root layout. We created ${bold( - `app${rootLayoutPath.replace(appDir, '')}` - )} for you.` - ) + '\n' + Log.warn( + `Your page ${bold( + `app/${normalizedPagePath}` + )} did not have a root layout. We created ${bold( + `app${rootLayoutPath.replace(appDir, '')}` + )} for you.` ) // Created root layout diff --git a/packages/next/src/server/app-render/action-encryption-utils.ts b/packages/next/src/server/app-render/action-encryption-utils.ts new file mode 100644 index 0000000000000..96afce9acd93e --- /dev/null +++ b/packages/next/src/server/app-render/action-encryption-utils.ts @@ -0,0 +1,197 @@ +import type { ActionManifest } from '../../build/webpack/plugins/flight-client-entry-plugin' +import type { ClientReferenceManifest } from '../../build/webpack/plugins/flight-manifest-plugin' + +// Keep the key in memory as it should never change during the lifetime of the server in +// both development and production. +let __next_loaded_action_key: CryptoKey +let __next_internal_development_raw_action_key: string + +export function arrayBufferToString(buffer: ArrayBuffer) { + const bytes = new Uint8Array(buffer) + const len = bytes.byteLength + + // @anonrig: V8 has a limit of 65535 arguments in a function. + // For len < 65535, this is faster. + // https://github.com/vercel/next.js/pull/56377#pullrequestreview-1656181623 + if (len < 65535) { + return String.fromCharCode.apply(null, bytes as unknown as number[]) + } + + let binary = '' + for (let i = 0; i < len; i++) { + binary += String.fromCharCode(bytes[i]) + } + return binary +} + +export function stringToUint8Array(binary: string) { + const len = binary.length + const arr = new Uint8Array(len) + + for (let i = 0; i < len; i++) { + arr[i] = binary.charCodeAt(i) + } + + return arr +} + +const encoder = new TextEncoder() + +export function encrypt(key: CryptoKey, salt: string, data: Uint8Array) { + const iv = encoder.encode(salt) + return crypto.subtle.encrypt( + { + name: 'AES-GCM', + iv, + }, + key, + data + ) +} + +export function decrypt(key: CryptoKey, salt: string, data: Uint8Array) { + const iv = encoder.encode(salt) + return crypto.subtle.decrypt( + { + name: 'AES-GCM', + iv, + }, + key, + data + ) +} + +export async function generateRandomActionKeyRaw(dev?: boolean) { + // For development, we just keep one key in memory for all actions. + // This makes things faster. + if (dev) { + if (typeof __next_internal_development_raw_action_key !== 'undefined') { + return __next_internal_development_raw_action_key + } + } + + const key = await crypto.subtle.generateKey( + { + name: 'AES-GCM', + length: 256, + }, + true, + ['encrypt', 'decrypt'] + ) + const exported = await crypto.subtle.exportKey('raw', key) + const b64 = btoa(arrayBufferToString(exported)) + + __next_loaded_action_key = key + if (dev) { + __next_internal_development_raw_action_key = b64 + } + + return b64 +} + +// This is a global singleton that is used to encode/decode the action bound args from +// the closure. This can't be using a AsyncLocalStorage as it might happen on the module +// level. Since the client reference manifest won't be mutated, let's use a global singleton +// to keep it. +const SERVER_ACTION_MANIFESTS_SINGLETON = Symbol.for( + 'next.server.action-manifests' +) + +export function setReferenceManifestsSingleton({ + clientReferenceManifest, + serverActionsManifest, + serverModuleMap, +}: { + clientReferenceManifest: ClientReferenceManifest + serverActionsManifest: ActionManifest + serverModuleMap: { + [id: string]: { + id: string + chunks: string[] + name: string + } + } +}) { + // @ts-ignore + globalThis[SERVER_ACTION_MANIFESTS_SINGLETON] = { + clientReferenceManifest, + serverActionsManifest, + serverModuleMap, + } +} + +export function getServerModuleMap() { + const serverActionsManifestSingleton = (globalThis as any)[ + SERVER_ACTION_MANIFESTS_SINGLETON + ] as { + serverModuleMap: { + [id: string]: { + id: string + chunks: string[] + name: string + } + } + } + + if (!serverActionsManifestSingleton) { + throw new Error( + 'Missing manifest for Server Actions. This is a bug in Next.js' + ) + } + + return serverActionsManifestSingleton.serverModuleMap +} + +export function getClientReferenceManifestSingleton() { + const serverActionsManifestSingleton = (globalThis as any)[ + SERVER_ACTION_MANIFESTS_SINGLETON + ] as { + clientReferenceManifest: ClientReferenceManifest + serverActionsManifest: ActionManifest + } + + if (!serverActionsManifestSingleton) { + throw new Error( + 'Missing manifest for Server Actions. This is a bug in Next.js' + ) + } + + return serverActionsManifestSingleton.clientReferenceManifest +} + +export async function getActionEncryptionKey() { + if (__next_loaded_action_key) { + return __next_loaded_action_key + } + + const serverActionsManifestSingleton = (globalThis as any)[ + SERVER_ACTION_MANIFESTS_SINGLETON + ] as { + clientReferenceManifest: ClientReferenceManifest + serverActionsManifest: ActionManifest + } + + if (!serverActionsManifestSingleton) { + throw new Error( + 'Missing manifest for Server Actions. This is a bug in Next.js' + ) + } + + const rawKey = + process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY || + serverActionsManifestSingleton.serverActionsManifest.encryptionKey + + if (rawKey === undefined) { + throw new Error('Missing encryption key for Server Actions') + } + + __next_loaded_action_key = await crypto.subtle.importKey( + 'raw', + stringToUint8Array(atob(rawKey)), + 'AES-GCM', + true, + ['encrypt', 'decrypt'] + ) + + return __next_loaded_action_key +} diff --git a/packages/next/src/server/app-render/action-encryption.ts b/packages/next/src/server/app-render/action-encryption.ts new file mode 100644 index 0000000000000..b8cad194ee38c --- /dev/null +++ b/packages/next/src/server/app-render/action-encryption.ts @@ -0,0 +1,109 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import 'server-only' + +/* eslint-disable import/no-extraneous-dependencies */ +import { + renderToReadableStream, + decodeReply, +} from 'react-server-dom-webpack/server.edge' +/* eslint-disable import/no-extraneous-dependencies */ +import { + createFromReadableStream, + encodeReply, +} from 'react-server-dom-webpack/client.edge' + +import { streamToString } from '../stream-utils/node-web-streams-helper' +import { + arrayBufferToString, + decrypt, + encrypt, + getActionEncryptionKey, + getClientReferenceManifestSingleton, + getServerModuleMap, + stringToUint8Array, +} from './action-encryption-utils' + +async function decodeActionBoundArg(actionId: string, arg: string) { + const key = await getActionEncryptionKey() + if (typeof key === 'undefined') { + throw new Error( + `Missing encryption key for Server Action. This is a bug in Next.js` + ) + } + + const decoded = await decrypt( + key, + '__next_action__' + actionId, + stringToUint8Array(atob(arg)) + ) + return arrayBufferToString(decoded) +} + +async function encodeActionBoundArg(actionId: string, arg: string) { + const key = await getActionEncryptionKey() + if (key === undefined) { + throw new Error( + `Missing encryption key for Server Action. This is a bug in Next.js` + ) + } + + const encoded = await encrypt( + key, + '__next_action__' + actionId, + stringToUint8Array(arg) + ) + return btoa(arrayBufferToString(encoded)) +} + +// Encrypts the action's bound args into a string. +export async function encryptActionBoundArgs(actionId: string, args: any[]) { + const clientReferenceManifestSingleton = getClientReferenceManifestSingleton() + + // Using Flight to serialize the args into a string. + const serialized = await streamToString( + renderToReadableStream(args, clientReferenceManifestSingleton.clientModules) + ) + + // Encrypt the serialized string with the action id as the salt. + const encryped = await encodeActionBoundArg(actionId, serialized) + + return encryped +} + +// Decrypts the action's bound args from the encrypted string. +export async function decryptActionBoundArgs( + actionId: string, + encryped: Promise +) { + // Decrypt the serialized string with the action id as the salt. + const decryped = await decodeActionBoundArg(actionId, await encryped) + + // Using Flight to deserialize the args from the string. + const deserialized = await createFromReadableStream( + new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode(decryped)) + controller.close() + }, + }), + { + ssrManifest: { + // TODO: We can't use the client reference manifest to resolve the modules + // on the server side - instead they need to be recovered as the module + // references (proxies) again. + // For now, we'll just use an empty module map. + moduleLoading: {}, + moduleMap: {}, + }, + } + ) + + // This extra step ensures that the server references are recovered. + const serverModuleMap = getServerModuleMap() + const transformed = await decodeReply( + await encodeReply(deserialized), + serverModuleMap + ) + + return transformed +} diff --git a/packages/next/src/server/app-render/action-handler.ts b/packages/next/src/server/app-render/action-handler.ts index 8737cb30dbc26..82438637897aa 100644 --- a/packages/next/src/server/app-render/action-handler.ts +++ b/packages/next/src/server/app-render/action-handler.ts @@ -222,8 +222,7 @@ export async function handleAction({ req, res, ComponentMod, - page, - serverActionsManifest, + serverModuleMap, generateFlight, staticGenerationStore, requestStore, @@ -233,8 +232,13 @@ export async function handleAction({ req: IncomingMessage res: ServerResponse ComponentMod: any - page: string - serverActionsManifest: any + serverModuleMap: { + [id: string]: { + id: string + chunks: string[] + name: string + } + } generateFlight: GenerateFlight staticGenerationStore: StaticGenerationStore requestStore: RequestStore @@ -318,22 +322,6 @@ export async function handleAction({ ) let bound = [] - const workerName = 'app' + page - const serverModuleMap = new Proxy( - {}, - { - get: (_, id: string) => { - return { - id: serverActionsManifest[ - process.env.NEXT_RUNTIME === 'edge' ? 'edge' : 'node' - ][id].workers[workerName], - name: id, - chunks: [], - } - }, - } - ) - const { actionAsyncStorage } = ComponentMod as { actionAsyncStorage: ActionAsyncStorage } @@ -458,13 +446,10 @@ export async function handleAction({ // / -> fire action -> POST / -> appRender1 -> modId for the action file // /foo -> fire action -> POST /foo -> appRender2 -> modId for the action file - // Get all workers that include this action - const actionWorkers = - serverActionsManifest[ - process.env.NEXT_RUNTIME === 'edge' ? 'edge' : 'node' - ][actionId] - - if (!actionWorkers) { + let actionModId: string + try { + actionModId = serverModuleMap[actionId].id + } catch (err) { // When this happens, it could be a deployment skew where the action came // from a different deployment. We'll just return a 404 with a message logged. console.error( @@ -475,7 +460,6 @@ export async function handleAction({ } } - const actionModId = actionWorkers.workers[workerName] const actionHandler = ComponentMod.__next_app__.require(actionModId)[actionId] diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index ca88145c5bd20..5eb8c795a2875 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -66,6 +66,7 @@ import { makeGetServerInsertedHTML } from './make-get-server-inserted-html' import { walkTreeWithFlightRouterState } from './walk-tree-with-flight-router-state' import { createComponentTree } from './create-component-tree' import { getAssetQueryString } from './get-asset-query-string' +import { setReferenceManifestsSingleton } from './action-encryption-utils' export type GetDynamicParamFromSegment = ( // [slug] / [[slug]] / [...slug] @@ -428,6 +429,34 @@ async function renderToHTMLOrFlightImpl( // TODO: fix this typescript const clientReferenceManifest = renderOpts.clientReferenceManifest! + const workerName = 'app' + renderOpts.page + const serverModuleMap: { + [id: string]: { + id: string + chunks: string[] + name: string + } + } = new Proxy( + {}, + { + get: (_, id: string) => { + return { + id: serverActionsManifest[ + process.env.NEXT_RUNTIME === 'edge' ? 'edge' : 'node' + ][id].workers[workerName], + name: id, + chunks: [], + } + }, + } + ) + + setReferenceManifestsSingleton({ + clientReferenceManifest, + serverActionsManifest, + serverModuleMap, + }) + const capturedErrors: Error[] = [] const allCapturedErrors: Error[] = [] const isNextExport = !!renderOpts.nextExport @@ -866,8 +895,7 @@ async function renderToHTMLOrFlightImpl( req, res, ComponentMod, - page: renderOpts.page, - serverActionsManifest, + serverModuleMap, generateFlight, staticGenerationStore: staticGenerationStore, requestStore: requestStore, diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index 07a52a8611c94..0ff81a5538897 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -686,7 +686,7 @@ export default abstract class Server { public logError(err: Error): void { if (this.quiet) return - console.error(err) + Log.error(err) } public async handleRequest( @@ -763,7 +763,7 @@ export default abstract class Server { return } if (name.toLowerCase() === 'set-cookie') { - const middlewareValue = getRequestMeta(req, '_nextMiddlewareCookie') + const middlewareValue = getRequestMeta(req, 'middlewareCookie') if ( !middlewareValue || @@ -847,7 +847,6 @@ export default abstract class Server { if (pathnameInfo.basePath) { req.url = removePathPrefix(req.url!, this.nextConfig.basePath) - addRequestMeta(req, '_nextHadBasePath', true) } const useMatchedPathHeader = @@ -950,9 +949,8 @@ export default abstract class Server { const rewriteParamKeys = Object.keys(rewriteParams) const didRewrite = pathnameBeforeRewrite !== parsedUrl.pathname - if (didRewrite) { - addRequestMeta(req, '_nextRewroteUrl', parsedUrl.pathname!) - addRequestMeta(req, '_nextDidRewrite', true) + if (didRewrite && parsedUrl.pathname) { + addRequestMeta(req, 'rewroteURL', parsedUrl.pathname) } const routeParamKeys = new Set() @@ -1105,11 +1103,11 @@ export default abstract class Server { } } - addRequestMeta(req, '__nextIsLocaleDomain', Boolean(domainLocale)) + addRequestMeta(req, 'isLocaleDomain', Boolean(domainLocale)) if (pathnameInfo.locale) { req.url = formatUrl(url) - addRequestMeta(req, '__nextStrippedLocale', true) + addRequestMeta(req, 'didStripLocale', true) } // If we aren't in minimal mode or there is no locale in the query @@ -1132,13 +1130,13 @@ export default abstract class Server { // cache can be leveraged locally if ( !(this.serverOptions as any).webServerConfig && - !getRequestMeta(req, '_nextIncrementalCache') + !getRequestMeta(req, 'incrementalCache') ) { let protocol: 'http:' | 'https:' = 'https:' try { const parsedFullUrl = new URL( - getRequestMeta(req, '__NEXT_INIT_URL') || '/', + getRequestMeta(req, 'initURL') || '/', 'http://n' ) protocol = parsedFullUrl.protocol as 'https:' | 'http:' @@ -1150,7 +1148,7 @@ export default abstract class Server { | 'http' | 'https', }) - addRequestMeta(req, '_nextIncrementalCache', incrementalCache) + addRequestMeta(req, 'incrementalCache', incrementalCache) ;(globalThis as any).__incrementalCache = incrementalCache } @@ -1202,8 +1200,7 @@ export default abstract class Server { if (parsedUrl.pathname !== parsedMatchedPath.pathname) { parsedUrl.pathname = parsedMatchedPath.pathname - addRequestMeta(req, '_nextRewroteUrl', invokePathnameInfo.pathname) - addRequestMeta(req, '_nextDidRewrite', true) + addRequestMeta(req, 'rewroteURL', invokePathnameInfo.pathname) } const normalizeResult = normalizeLocalePath( removePathPrefix(parsedUrl.pathname, this.nextConfig.basePath || ''), @@ -1589,8 +1586,7 @@ export default abstract class Server { // and we need to look up the path by the rewritten path let urlPathname = parseUrl(req.url || '').pathname || '/' - let resolvedUrlPathname = - getRequestMeta(req, '_nextRewroteUrl') || urlPathname + let resolvedUrlPathname = getRequestMeta(req, 'rewroteURL') || urlPathname let staticPaths: string[] | undefined @@ -1917,7 +1913,7 @@ export default abstract class Server { try { const parsedFullUrl = new URL( - getRequestMeta(req, '__NEXT_INIT_URL') || '/', + getRequestMeta(req, 'initURL') || '/', 'http://n' ) protocol = parsedFullUrl.protocol as 'https:' | 'http:' @@ -2650,9 +2646,9 @@ export default abstract class Server { page, url: ctx.req.url, matchedPath: ctx.req.headers['x-matched-path'], - initUrl: getRequestMeta(ctx.req, '__NEXT_INIT_URL'), - didRewrite: getRequestMeta(ctx.req, '_nextDidRewrite'), - rewroteUrl: getRequestMeta(ctx.req, '_nextRewroteUrl'), + initUrl: getRequestMeta(ctx.req, 'initURL'), + didRewrite: !!getRequestMeta(ctx.req, 'rewroteURL'), + rewroteUrl: getRequestMeta(ctx.req, 'rewroteURL'), }, null, 2 @@ -2921,12 +2917,12 @@ export default abstract class Server { // If the page has a route module, use it for the new match. If it doesn't // have a route module, remove the match. if (result.components.routeModule) { - addRequestMeta(ctx.req, '_nextMatch', { + addRequestMeta(ctx.req, 'match', { definition: result.components.routeModule.definition, params: undefined, }) } else { - removeRequestMeta(ctx.req, '_nextMatch') + removeRequestMeta(ctx.req, 'match') } try { @@ -2959,7 +2955,7 @@ export default abstract class Server { if (fallbackComponents) { // There was an error, so use it's definition from the route module // to add the match to the request. - addRequestMeta(ctx.req, '_nextMatch', { + addRequestMeta(ctx.req, 'match', { definition: fallbackComponents.routeModule!.definition, params: undefined, }) diff --git a/packages/next/src/server/future/route-modules/app-route/module.ts b/packages/next/src/server/future/route-modules/app-route/module.ts index 4caf96f791a59..0109bddb4782f 100644 --- a/packages/next/src/server/future/route-modules/app-route/module.ts +++ b/packages/next/src/server/future/route-modules/app-route/module.ts @@ -436,7 +436,7 @@ export class AppRouteRouteModule extends RouteModule< // // Relativize the url so it's relative to the base url. This is so the // // outgoing headers upstream can be relative. // const rewritePath = response.headers.get('x-middleware-rewrite')! - // const initUrl = getRequestMeta(req, '__NEXT_INIT_URL')! + // const initUrl = getRequestMeta(req, 'initURL')! // const { pathname } = parseUrl(relativizeURL(rewritePath, initUrl)) // response.headers.set('x-middleware-rewrite', pathname) } diff --git a/packages/next/src/server/lib/is-node-debugging.ts b/packages/next/src/server/lib/is-node-debugging.ts deleted file mode 100644 index 0dcc6a0d96164..0000000000000 --- a/packages/next/src/server/lib/is-node-debugging.ts +++ /dev/null @@ -1,14 +0,0 @@ -export function checkIsNodeDebugging() { - let isNodeDebugging: 'brk' | boolean = !!( - process.execArgv.some((localArg) => localArg.startsWith('--inspect')) || - process.env.NODE_OPTIONS?.match?.(/--inspect(=\S+)?( |$)/) - ) - - if ( - process.execArgv.some((localArg) => localArg.startsWith('--inspect-brk')) || - process.env.NODE_OPTIONS?.match?.(/--inspect-brk(=\S+)?( |$)/) - ) { - isNodeDebugging = 'brk' - } - return isNodeDebugging -} diff --git a/packages/next/src/server/lib/route-resolver.ts b/packages/next/src/server/lib/route-resolver.ts index 8a27b45b70b19..32334e0fca3b1 100644 --- a/packages/next/src/server/lib/route-resolver.ts +++ b/packages/next/src/server/lib/route-resolver.ts @@ -235,7 +235,7 @@ export async function makeResolver( res, parsedUrl, undefined, - getRequestMeta(req, '__NEXT_CLONABLE_BODY')?.cloneBodyStream(), + getRequestMeta(req, 'clonableBody')?.cloneBodyStream(), nextConfig.experimental.proxyTimeout ) return diff --git a/packages/next/src/server/lib/router-server.ts b/packages/next/src/server/lib/router-server.ts index 61a4c5a0311be..2bb8a6e3dbcc4 100644 --- a/packages/next/src/server/lib/router-server.ts +++ b/packages/next/src/server/lib/router-server.ts @@ -63,8 +63,6 @@ export async function initialize(opts: { experimentalTestProxy?: boolean experimentalHttpsServer?: boolean }): Promise<[WorkerRequestHandler, WorkerUpgradeHandler]> { - process.title = 'next-router-worker' - if (!process.env.NODE_ENV) { // @ts-ignore not readonly process.env.NODE_ENV = opts.dev ? 'development' : 'production' @@ -371,7 +369,7 @@ export async function initialize(opts: { res, parsedUrl, undefined, - getRequestMeta(req, '__NEXT_CLONABLE_BODY')?.cloneBodyStream(), + getRequestMeta(req, 'clonableBody')?.cloneBodyStream(), config.experimental.proxyTimeout ) } diff --git a/packages/next/src/server/lib/router-utils/resolve-routes.ts b/packages/next/src/server/lib/router-utils/resolve-routes.ts index da7373f6935a3..ed8b901728506 100644 --- a/packages/next/src/server/lib/router-utils/resolve-routes.ts +++ b/packages/next/src/server/lib/router-utils/resolve-routes.ts @@ -146,12 +146,12 @@ export function getResolveRoutes( }${req.url}` : req.url || '' - addRequestMeta(req, '__NEXT_INIT_URL', initUrl) - addRequestMeta(req, '__NEXT_INIT_QUERY', { ...parsedUrl.query }) - addRequestMeta(req, '_protocol', protocol) + addRequestMeta(req, 'initURL', initUrl) + addRequestMeta(req, 'initQuery', { ...parsedUrl.query }) + addRequestMeta(req, 'initProtocol', protocol) if (!isUpgradeReq) { - addRequestMeta(req, '__NEXT_CLONABLE_BODY', getCloneableBody(req)) + addRequestMeta(req, 'clonableBody', getCloneableBody(req)) } const maybeAddTrailingSlash = (pathname: string) => { diff --git a/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts b/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts index 0ca985eb09d75..65d9ff6af77af 100644 --- a/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts +++ b/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts @@ -348,11 +348,12 @@ async function startWatcher(opts: SetupOpts) { relevantIssues.add(formatted) } - for (const issue of oldSet.keys()) { - if (!newSet.has(issue)) { - console.error(`✅ ${displayName} fixed ${issue}`) - } - } + // TODO: Format these messages correctly. + // for (const issue of oldSet.keys()) { + // if (!newSet.has(issue)) { + // console.error(`✅ ${displayName} fixed ${issue}`) + // } + // } if (relevantIssues.size && throwIssue) { throw new ModuleBuildError([...relevantIssues].join('\n\n')) @@ -695,6 +696,7 @@ async function startWatcher(opts: SetupOpts) { const manifest: ActionManifest = { node: {}, edge: {}, + encryptionKey: '', } function mergeActionIds( @@ -702,7 +704,10 @@ async function startWatcher(opts: SetupOpts) { other: ActionEntries ): void { for (const key in other) { - const action = (actionEntries[key] ??= { workers: {}, layer: {} }) + const action = (actionEntries[key] ??= { + workers: {}, + layer: {}, + }) Object.assign(action.workers, other[key].workers) Object.assign(action.layer, other[key].layer) } @@ -711,6 +716,7 @@ async function startWatcher(opts: SetupOpts) { for (const m of manifests) { mergeActionIds(manifest.node, m.node) mergeActionIds(manifest.edge, m.edge) + manifest.encryptionKey = m.encryptionKey } return manifest @@ -2188,20 +2194,24 @@ async function startWatcher(opts: SetupOpts) { ) let originalFrame, isEdgeCompiler - if (frame?.lineNumber && frame?.file) { + const frameFile = frame?.file + if (frame?.lineNumber && frameFile) { if (opts.turbo) { try { - originalFrame = await createOriginalTurboStackFrame( - project!, - frame - ) + originalFrame = await createOriginalTurboStackFrame(project!, { + file: frameFile, + methodName: frame.methodName, + line: frame.lineNumber ?? 0, + column: frame.column, + isServer: true, + }) } catch {} } else { - const moduleId = frame.file!.replace( + const moduleId = frameFile.replace( /^(webpack-internal:\/\/\/|file:\/\/)/, '' ) - const modulePath = frame.file.replace( + const modulePath = frameFile.replace( /^(webpack-internal:\/\/\/|file:\/\/)(\(.*\)\/)?/, '' ) diff --git a/packages/next/src/server/lib/start-server.ts b/packages/next/src/server/lib/start-server.ts index a0a6cc0abdbeb..4d99796835f34 100644 --- a/packages/next/src/server/lib/start-server.ts +++ b/packages/next/src/server/lib/start-server.ts @@ -15,10 +15,9 @@ import https from 'https' import Watchpack from 'watchpack' import * as Log from '../../build/output/log' import setupDebug from 'next/dist/compiled/debug' -import { RESTART_EXIT_CODE, getDebugPort } from './utils' +import { RESTART_EXIT_CODE, checkNodeDebugType, getDebugPort } from './utils' import { formatHostname } from './format-hostname' import { initialize } from './router-server' -import { checkIsNodeDebugging } from './is-node-debugging' import { CONFIG_FILES } from '../../shared/lib/constants' import { getStartServerInfo, logStartInfo } from './app-info-log' @@ -86,6 +85,7 @@ export async function startServer({ isExperimentalTestProxy, selfSignedCertificate, }: StartServerOptions): Promise { + process.title = 'next-server' let handlersReady = () => {} let handlersError = () => {} @@ -183,7 +183,7 @@ export async function startServer({ } }) - const isNodeDebugging = checkIsNodeDebugging() + const nodeDebugType = checkNodeDebugType() await new Promise((resolve) => { server.on('listening', async () => { @@ -207,12 +207,10 @@ export async function startServer({ selfSignedCertificate ? 'https' : 'http' }://${formattedHostname}:${port}` - if (isNodeDebugging) { + if (nodeDebugType) { const debugPort = getDebugPort() Log.info( - `the --inspect${ - isNodeDebugging === 'brk' ? '-brk' : '' - } option was detected, the Next.js router server should be inspected at port ${debugPort}.` + `the --${nodeDebugType} option was detected, the Next.js router server should be inspected at port ${debugPort}.` ) } @@ -243,7 +241,7 @@ export async function startServer({ server, hostname, minimalMode, - isNodeDebugging: Boolean(isNodeDebugging), + isNodeDebugging: Boolean(nodeDebugType), keepAliveTimeout, experimentalTestProxy: !!isExperimentalTestProxy, experimentalHttpsServer: !!selfSignedCertificate, diff --git a/packages/next/src/server/lib/utils.ts b/packages/next/src/server/lib/utils.ts index 18d09ceb8526a..8b101ebc1a7ce 100644 --- a/packages/next/src/server/lib/utils.ts +++ b/packages/next/src/server/lib/utils.ts @@ -42,3 +42,23 @@ export function getPort(args: arg.Result): number { } export const RESTART_EXIT_CODE = 77 + +export function checkNodeDebugType() { + let nodeDebugType = undefined + + if ( + process.execArgv.some((localArg) => localArg.startsWith('--inspect')) || + process.env.NODE_OPTIONS?.match?.(/--inspect(=\S+)?( |$)/) + ) { + nodeDebugType = 'inspect' + } + + if ( + process.execArgv.some((localArg) => localArg.startsWith('--inspect-brk')) || + process.env.NODE_OPTIONS?.match?.(/--inspect-brk(=\S+)?( |$)/) + ) { + nodeDebugType = 'inspect-brk' + } + + return nodeDebugType +} diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index 10494c86a88d8..b987f1021358e 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -872,7 +872,7 @@ export default class NextNodeServer extends BaseServer { // Add the match to the request so we don't have to re-run the matcher // for the same request. - addRequestMeta(req, '_nextMatch', match) + addRequestMeta(req, 'match', match) // TODO-APP: move this to a route handler const edgeFunctionsPages = this.getEdgeFunctionsPages() @@ -1472,13 +1472,13 @@ export default class NextNodeServer extends BaseServer { let url: string if (this.nextConfig.skipMiddlewareUrlNormalize) { - url = getRequestMeta(params.request, '__NEXT_INIT_URL')! + url = getRequestMeta(params.request, 'initURL')! } else { // For middleware to "fetch" we must always provide an absolute URL const query = urlQueryToSearchParams(params.parsed.query).toString() const locale = params.parsed.query.__nextLocale - url = `${getRequestMeta(params.request, '_protocol')}://${ + url = `${getRequestMeta(params.request, 'initProtocol')}://${ this.fetchHostname || 'localhost' }:${this.port}${locale ? `/${locale}` : ''}${params.parsed.pathname}${ query ? `?${query}` : '' @@ -1532,7 +1532,7 @@ export default class NextNodeServer extends BaseServer { }, url: url, page, - body: getRequestMeta(params.request, '__NEXT_CLONABLE_BODY'), + body: getRequestMeta(params.request, 'clonableBody'), signal: signalFromNodeResponse( (params.response as NodeNextResponse).originalResponse ), @@ -1565,7 +1565,7 @@ export default class NextNodeServer extends BaseServer { } // Add cookies to request meta. - addRequestMeta(params.request, '_nextMiddlewareCookie', cookies) + addRequestMeta(params.request, 'middlewareCookie', cookies) } return result @@ -1596,7 +1596,7 @@ export default class NextNodeServer extends BaseServer { return handleFinished() } - const initUrl = getRequestMeta(req, '__NEXT_INIT_URL')! + const initUrl = getRequestMeta(req, 'initURL')! const parsedUrl = parseUrl(initUrl) const pathnameInfo = getNextPathnameInfo(parsedUrl.pathname, { nextConfig: this.nextConfig, @@ -1758,12 +1758,12 @@ export default class NextNodeServer extends BaseServer { ? `https://${req.headers.host || 'localhost'}${req.url}` : req.url - addRequestMeta(req, '__NEXT_INIT_URL', initUrl) - addRequestMeta(req, '__NEXT_INIT_QUERY', { ...parsedUrl.query }) - addRequestMeta(req, '_protocol', protocol) + addRequestMeta(req, 'initURL', initUrl) + addRequestMeta(req, 'initQuery', { ...parsedUrl.query }) + addRequestMeta(req, 'initProtocol', protocol) if (!isUpgradeReq) { - addRequestMeta(req, '__NEXT_CLONABLE_BODY', getCloneableBody(req.body)) + addRequestMeta(req, 'clonableBody', getCloneableBody(req.body)) } } @@ -1800,7 +1800,7 @@ export default class NextNodeServer extends BaseServer { // For edge to "fetch" we must always provide an absolute URL const isDataReq = !!query.__nextDataReq const initialUrl = new URL( - getRequestMeta(params.req, '__NEXT_INIT_URL') || '/', + getRequestMeta(params.req, 'initURL') || '/', 'http://n' ) const queryString = urlQueryToSearchParams({ @@ -1840,7 +1840,7 @@ export default class NextNodeServer extends BaseServer { name: params.page, ...(params.params && { params: params.params }), }, - body: getRequestMeta(params.req, '__NEXT_CLONABLE_BODY'), + body: getRequestMeta(params.req, 'clonableBody'), signal: signalFromNodeResponse( (params.res as NodeNextResponse).originalResponse ), @@ -1849,7 +1849,7 @@ export default class NextNodeServer extends BaseServer { onWarning: params.onWarning, incrementalCache: (globalThis as any).__incrementalCache || - getRequestMeta(params.req, '_nextIncrementalCache'), + getRequestMeta(params.req, 'incrementalCache'), }) if (result.fetchMetrics) { diff --git a/packages/next/src/server/next.ts b/packages/next/src/server/next.ts index 391a137f95bc9..cb8053c6b8e10 100644 --- a/packages/next/src/server/next.ts +++ b/packages/next/src/server/next.ts @@ -25,7 +25,7 @@ import { PHASE_PRODUCTION_SERVER } from '../shared/lib/constants' import { getTracer } from './lib/trace/tracer' import { NextServerSpan } from './lib/trace/constants' import { formatUrl } from '../shared/lib/router/utils/format-url' -import { checkIsNodeDebugging } from './lib/is-node-debugging' +import { checkNodeDebugType } from './lib/utils' let ServerImpl: typeof Server @@ -275,7 +275,7 @@ class NextCustomServer extends NextServer { const { getRequestHandlers } = require('./lib/start-server') as typeof import('./lib/start-server') - const isNodeDebugging = checkIsNodeDebugging() + const isNodeDebugging = !!checkNodeDebugType() const initResult = await getRequestHandlers({ dir: this.options.dir!, diff --git a/packages/next/src/server/render.tsx b/packages/next/src/server/render.tsx index 9af67e0e07861..96c39c32ca9d2 100644 --- a/packages/next/src/server/render.tsx +++ b/packages/next/src/server/render.tsx @@ -660,7 +660,7 @@ export async function renderToHTMLImpl( renderOpts.defaultLocale, renderOpts.domainLocales, isPreview, - getRequestMeta(req, '__nextIsLocaleDomain') + getRequestMeta(req, 'isLocaleDomain') ) const appRouter = adaptForAppRouterInstance(router) @@ -1476,7 +1476,7 @@ export async function renderToHTMLImpl( docComponentsRendered, dangerousAsPath: router.asPath, canonicalBase: - !renderOpts.ampPath && getRequestMeta(req, '__nextStrippedLocale') + !renderOpts.ampPath && getRequestMeta(req, 'didStripLocale') ? `${renderOpts.canonicalBase || ''}/${renderOpts.locale}` : renderOpts.canonicalBase, ampPath, diff --git a/packages/next/src/server/request-meta.ts b/packages/next/src/server/request-meta.ts index a2fa7372e3d00..c3c70c0a66a81 100644 --- a/packages/next/src/server/request-meta.ts +++ b/packages/next/src/server/request-meta.ts @@ -15,31 +15,58 @@ export type NextIncomingMessage = (BaseNextRequest | IncomingMessage) & { } export interface RequestMeta { - __NEXT_INIT_QUERY?: ParsedUrlQuery - __NEXT_INIT_URL?: string - __NEXT_CLONABLE_BODY?: CloneableBody - __nextHadTrailingSlash?: boolean + /** + * The query that was used to make the request. + */ + initQuery?: ParsedUrlQuery + + /** + * The URL that was used to make the request. + */ + initURL?: string + + /** + * The protocol that was used to make the request. + */ + initProtocol?: string + + /** + * The body that was read from the request. This is used to allow the body to + * be read multiple times. + */ + clonableBody?: CloneableBody /** * True when the request matched a locale domain that was configured in the * next.config.js file. */ - __nextIsLocaleDomain?: boolean + isLocaleDomain?: boolean /** * True when the request had locale information stripped from the pathname * part of the URL. */ - __nextStrippedLocale?: boolean - _nextDidRewrite?: boolean - _nextHadBasePath?: boolean - _nextRewroteUrl?: string - _nextMiddlewareCookie?: string[] - _protocol?: string - _nextDataNormalizing?: boolean - _nextMatch?: RouteMatch - _nextIncrementalCache?: any - _nextMinimalMode?: boolean + didStripLocale?: boolean + + /** + * If the request had it's URL rewritten, this is the URL it was rewritten to. + */ + rewroteURL?: string + + /** + * The cookies that were added by middleware and were added to the response. + */ + middlewareCookie?: string[] + + /** + * The match on the request for a given route. + */ + match?: RouteMatch + + /** + * The incremental cache to use for the request. + */ + incrementalCache?: any } /** diff --git a/packages/next/src/server/server-route-utils.ts b/packages/next/src/server/server-route-utils.ts index 7bf1f8da276b1..f96a4a31f473a 100644 --- a/packages/next/src/server/server-route-utils.ts +++ b/packages/next/src/server/server-route-utils.ts @@ -8,7 +8,7 @@ import { stringify as stringifyQs } from 'querystring' // we need to re-encode them here but still allow passing through // values from rewrites/redirects export const stringifyQuery = (req: BaseNextRequest, query: ParsedUrlQuery) => { - const initialQuery = getRequestMeta(req, '__NEXT_INIT_QUERY') || {} + const initialQuery = getRequestMeta(req, 'initQuery') || {} const initialQueryValues = Object.values(initialQuery) return stringifyQs(query, undefined, undefined, { diff --git a/packages/next/src/server/web-server.ts b/packages/next/src/server/web-server.ts index 17f01574a1cc2..ea0439d08fccf 100644 --- a/packages/next/src/server/web-server.ts +++ b/packages/next/src/server/web-server.ts @@ -110,7 +110,7 @@ export default class NextWebServer extends BaseServer { req: WebNextRequest, parsedUrl: NextUrlWithParsedQuery ) { - addRequestMeta(req, '__NEXT_INIT_QUERY', { ...parsedUrl.query }) + addRequestMeta(req, 'initQuery', { ...parsedUrl.query }) } protected getPrerenderManifest() { diff --git a/packages/next/src/server/web/spec-extension/adapters/next-request.ts b/packages/next/src/server/web/spec-extension/adapters/next-request.ts index c74a312c4a11a..ab942626e9a5a 100644 --- a/packages/next/src/server/web/spec-extension/adapters/next-request.ts +++ b/packages/next/src/server/web/spec-extension/adapters/next-request.ts @@ -84,7 +84,7 @@ export class NextRequestAdapter { url = new URL(request.url) } else { // Grab the full URL from the request metadata. - const base = getRequestMeta(request, '__NEXT_INIT_URL') + const base = getRequestMeta(request, 'initURL') if (!base || !base.startsWith('http')) { // Because the URL construction relies on the fact that the URL provided // is absolute, we need to provide a base URL. We can't use the request diff --git a/packages/react-dev-overlay/package.json b/packages/react-dev-overlay/package.json index 16ec8cf820bc8..e623465158b93 100644 --- a/packages/react-dev-overlay/package.json +++ b/packages/react-dev-overlay/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-dev-overlay", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "description": "A development-only overlay for developing React applications.", "repository": { "url": "vercel/next.js", diff --git a/packages/react-dev-overlay/src/internal/helpers/parseStack.ts b/packages/react-dev-overlay/src/internal/helpers/parseStack.ts index c10226b3a3486..853022a0abca1 100644 --- a/packages/react-dev-overlay/src/internal/helpers/parseStack.ts +++ b/packages/react-dev-overlay/src/internal/helpers/parseStack.ts @@ -1,7 +1,7 @@ import type { StackFrame } from 'stacktrace-parser' import { parse } from 'stacktrace-parser' -const regexNextStatic = /\/_next(\/static\/.+)/g +const regexNextStatic = /\/_next(\/static\/.+)/ export function parseStack(stack: string): StackFrame[] { const frames = parse(stack) diff --git a/packages/react-dev-overlay/src/middleware-turbopack.ts b/packages/react-dev-overlay/src/middleware-turbopack.ts index 0d9504d8b7cf5..729ccdd8a1a4d 100644 --- a/packages/react-dev-overlay/src/middleware-turbopack.ts +++ b/packages/react-dev-overlay/src/middleware-turbopack.ts @@ -1,5 +1,4 @@ import type { IncomingMessage, ServerResponse } from 'http' -import type { StackFrame } from 'stacktrace-parser' import type { ParsedUrlQuery } from 'querystring' import type { OriginalStackFrameResponse } from './middleware' @@ -10,29 +9,30 @@ import { launchEditor } from './internal/helpers/launchEditor' interface Project { getSourceForAsset(filePath: string): Promise - traceSource(stackFrame: RustStackFrame): Promise + traceSource( + stackFrame: TurbopackStackFrame + ): Promise } -interface RustStackFrame { +interface TurbopackStackFrame { + column: number | null file: string - methodName: string | null + isServer: boolean line: number - column: number | null + methodName: string | null } const currentSourcesByFile: Map> = new Map() -async function batchedTraceSource(project: Project, frame: StackFrame) { - const file = frame.file +async function batchedTraceSource( + project: Project, + frame: TurbopackStackFrame +) { + const file = frame.file ? decodeURIComponent(frame.file) : undefined if (!file) { return } - const sourceFrame = await project.traceSource({ - file, - methodName: frame.methodName, - line: frame.lineNumber ?? 0, - column: frame.column, - }) + const sourceFrame = await project.traceSource(frame) if (!sourceFrame) { return @@ -60,7 +60,7 @@ async function batchedTraceSource(project: Project, frame: StackFrame) { file: sourceFrame.file, lineNumber: sourceFrame.line, column: sourceFrame.column, - methodName: sourceFrame.methodName ?? frame.methodName, + methodName: sourceFrame.methodName ?? frame.methodName ?? '', arguments: [], }, source: source ?? null, @@ -69,7 +69,7 @@ async function batchedTraceSource(project: Project, frame: StackFrame) { export async function createOriginalStackFrame( project: Project, - frame: StackFrame + frame: TurbopackStackFrame ): Promise { const traced = await batchedTraceSource(project, frame) if (!traced) { @@ -94,17 +94,15 @@ export async function createOriginalStackFrame( } } -function stackFrameFromQuery(query: ParsedUrlQuery): StackFrame { +function stackFrameFromQuery(query: ParsedUrlQuery): TurbopackStackFrame { return { file: query.file as string, methodName: query.methodName as string, - arguments: query.arguments as string[], - lineNumber: - typeof query.lineNumber === 'string' - ? parseInt(query.lineNumber, 10) - : null, + line: + typeof query.lineNumber === 'string' ? parseInt(query.lineNumber, 10) : 0, column: typeof query.column === 'string' ? parseInt(query.column, 10) : null, + isServer: query.isServer === 'true', } } @@ -158,7 +156,7 @@ export function getOverlayMiddleware(project: Project) { } try { - launchEditor(filePath, frame.lineNumber ?? 1, frame.column ?? 1) + launchEditor(filePath, frame.line, frame.column ?? 1) } catch (err) { console.log('Failed to launch editor:', err) res.statusCode = 500 diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index a76592572e0c7..621afdb9ebd2e 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": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "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 50ba5f67d22c1..c2b6d6576e112 100644 --- a/packages/third-parties/package.json +++ b/packages/third-parties/package.json @@ -1,6 +1,6 @@ { "name": "@next/third-parties", - "version": "13.5.7-canary.12", + "version": "13.5.7-canary.13", "repository": { "url": "vercel/next.js", "directory": "packages/third-parties" @@ -22,7 +22,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "13.5.7-canary.12", + "next": "13.5.7-canary.13", "outdent": "0.8.0", "prettier": "2.5.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ad5fc5bc41e5..40315a8f56aeb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -735,7 +735,7 @@ importers: packages/eslint-config-next: dependencies: '@next/eslint-plugin-next': - specifier: 13.5.7-canary.12 + specifier: 13.5.7-canary.13 version: link:../eslint-plugin-next '@rushstack/eslint-patch': specifier: ^1.3.3 @@ -796,7 +796,7 @@ importers: packages/next: dependencies: '@next/env': - specifier: 13.5.7-canary.12 + specifier: 13.5.7-canary.13 version: link:../next-env '@swc/helpers': specifier: 0.5.2 @@ -920,19 +920,19 @@ importers: specifier: 1.1.0 version: 1.1.0 '@next/polyfill-module': - specifier: 13.5.7-canary.12 + specifier: 13.5.7-canary.13 version: link:../next-polyfill-module '@next/polyfill-nomodule': - specifier: 13.5.7-canary.12 + specifier: 13.5.7-canary.13 version: link:../next-polyfill-nomodule '@next/react-dev-overlay': - specifier: 13.5.7-canary.12 + specifier: 13.5.7-canary.13 version: link:../react-dev-overlay '@next/react-refresh-utils': - specifier: 13.5.7-canary.12 + specifier: 13.5.7-canary.13 version: link:../react-refresh-utils '@next/swc': - specifier: 13.5.7-canary.12 + specifier: 13.5.7-canary.13 version: link:../next-swc '@opentelemetry/api': specifier: 1.4.1 @@ -1061,8 +1061,8 @@ importers: specifier: 0.22.6 version: 0.22.6 '@vercel/turbopack-ecmascript-runtime': - specifier: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.3 - version: '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.3(react-refresh@0.12.0)(webpack@5.86.0)' + specifier: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.4 + version: '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.4(react-refresh@0.12.0)(webpack@5.86.0)' acorn: specifier: 8.5.0 version: 8.5.0 @@ -1583,7 +1583,7 @@ importers: version: 1.0.20 devDependencies: next: - specifier: 13.5.7-canary.12 + specifier: 13.5.7-canary.13 version: link:../next outdent: specifier: 0.8.0 @@ -24947,9 +24947,9 @@ packages: /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.3(react-refresh@0.12.0)(webpack@5.86.0)': - resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.3} - id: '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.3' + '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.4(react-refresh@0.12.0)(webpack@5.86.0)': + resolution: {tarball: https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.4} + id: '@gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-231020.4' name: '@vercel/turbopack-ecmascript-runtime' version: 0.0.0 dependencies: diff --git a/test/development/basic/node-builtins.test.ts b/test/development/basic/node-builtins.test.ts index 3c5c6a6a335f8..982fa6c391e25 100644 --- a/test/development/basic/node-builtins.test.ts +++ b/test/development/basic/node-builtins.test.ts @@ -86,7 +86,7 @@ createNextDescribe( expect(parsedData.https).toBe(true) expect(parsedData.os).toBe('\n') expect(parsedData.path).toBe('/hello/world/test.txt') - expect(parsedData.process).toInclude('next-router-worker') + expect(parsedData.process).toInclude('next-server') expect(parsedData.querystring).toBe('a=b') expect(parsedData.stringDecoder).toBe(true) expect(parsedData.sys).toBe(true) @@ -112,7 +112,7 @@ createNextDescribe( expect(parsedData.https).toBe(true) expect(parsedData.os).toBe('\n') expect(parsedData.path).toBe('/hello/world/test.txt') - expect(parsedData.process).toInclude('next-router-worker') + expect(parsedData.process).toInclude('next-server') expect(parsedData.querystring).toBe('a=b') expect(parsedData.stringDecoder).toBe(true) expect(parsedData.sys).toBe(true) diff --git a/test/e2e/app-dir/actions/app-action.test.ts b/test/e2e/app-dir/actions/app-action.test.ts index 2ca61d6c9c539..89802c286f463 100644 --- a/test/e2e/app-dir/actions/app-action.test.ts +++ b/test/e2e/app-dir/actions/app-action.test.ts @@ -819,5 +819,13 @@ createNextDescribe( } ) }) + + describe('encryption', () => { + it('should send encrypted values from the closed over closure', async () => { + const res = await next.fetch('/encryption') + const html = await res.text() + expect(html).not.toContain('qwerty123') + }) + }) } ) diff --git a/test/e2e/app-dir/actions/app/encryption/page.js b/test/e2e/app-dir/actions/app/encryption/page.js new file mode 100644 index 0000000000000..77b8c56111e53 --- /dev/null +++ b/test/e2e/app-dir/actions/app/encryption/page.js @@ -0,0 +1,15 @@ +export default function Page() { + const secret = 'my password is qwerty123' + + return ( +
{ + 'use server' + console.log(secret) + return 'success' + }} + > + +
+ ) +} diff --git a/test/e2e/app-dir/create-root-layout/create-root-layout.test.ts b/test/e2e/app-dir/create-root-layout/create-root-layout.test.ts index 42066205b13d7..f7fc2ade9653e 100644 --- a/test/e2e/app-dir/create-root-layout/create-root-layout.test.ts +++ b/test/e2e/app-dir/create-root-layout/create-root-layout.test.ts @@ -25,10 +25,6 @@ describe('app-dir create root layout', () => { path.join(__dirname, 'next.config.js') ), }, - dependencies: { - react: 'latest', - 'react-dom': 'latest', - }, }) }) afterAll(() => next.destroy()) diff --git a/test/e2e/app-dir/metadata/metadata.test.ts b/test/e2e/app-dir/metadata/metadata.test.ts index 2065d6cdf4277..3d5887ade7076 100644 --- a/test/e2e/app-dir/metadata/metadata.test.ts +++ b/test/e2e/app-dir/metadata/metadata.test.ts @@ -523,7 +523,7 @@ createNextDescribe( }) // favicon shouldn't be overridden - expect($('link[rel="icon"]').attr('href')).toBe('/favicon.ico') + expect($('link[rel="icon"]').attr('href')).toMatch('/favicon.ico') }) it('should override file based images when opengraph-image and twitter-image specify images property', async () => { @@ -671,18 +671,20 @@ createNextDescribe( it('should support root level of favicon.ico', async () => { let $ = await next.render$('/') const favIcon = $('link[rel="icon"]') - expect(favIcon.attr('href')).toBe('/favicon.ico') + expect(favIcon.attr('href')).toMatch('/favicon.ico') expect(favIcon.attr('type')).toBe('image/x-icon') - expect(favIcon.attr('sizes')).toBe('16x16') + // Turbopack renders / emits image differently + expect(['16x16', '48x48']).toContain(favIcon.attr('sizes')) const iconSvg = $('link[rel="icon"][type="image/svg+xml"]') - expect(iconSvg.attr('href')).toBe('/icon.svg?90699bff34adba1f') - expect(iconSvg.attr('sizes')).toBe('any') + expect(iconSvg.attr('href')).toMatch('/icon.svg?') + // Turbopack renders / emits image differently + expect(['any', '48x48']).toContain(iconSvg.attr('sizes')) $ = await next.render$('/basic') const icon = $('link[rel="icon"]') - expect(icon.attr('href')).toBe('/favicon.ico') - expect(icon.attr('sizes')).toBe('16x16') + expect(icon.attr('href')).toMatch('/favicon.ico') + expect(['16x16', '48x48']).toContain(favIcon.attr('sizes')) if (!isNextDeploy) { const faviconFileBuffer = await fs.readFile( diff --git a/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts b/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts index 3db7101601ced..b8d7c7f336adf 100644 --- a/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts +++ b/test/e2e/app-dir/rsc-basic/rsc-basic.test.ts @@ -557,15 +557,19 @@ createNextDescribe( ).toBe('count: 1') }) - it('should support webpack loader rules', async () => { - const browser = await next.browser('/loader-rule') - - expect( - await browser.eval( - `window.getComputedStyle(document.querySelector('#red')).color` - ) - ).toBe('rgb(255, 0, 0)') - }) + // Skip as Turbopack doesn't support webpack loaders. + ;(process.env.TURBOPACK ? it.skip : it)( + 'should support webpack loader rules', + async () => { + const browser = await next.browser('/loader-rule') + + expect( + await browser.eval( + `window.getComputedStyle(document.querySelector('#red')).color` + ) + ).toBe('rgb(255, 0, 0)') + } + ) if (isNextStart) { it('should generate edge SSR manifests for Node.js', async () => { diff --git a/test/integration/externals-pages-bundle/test/externals.test.js b/test/integration/externals-pages-bundle/test/externals.test.js index 2b698534e4166..2ecf4745fed65 100644 --- a/test/integration/externals-pages-bundle/test/externals.test.js +++ b/test/integration/externals-pages-bundle/test/externals.test.js @@ -21,7 +21,7 @@ describe('default', () => { const app = await launchApp(appDir, port) await renderViaHTTP(port, '/') if (process.env.TURBOPACK) { - const ssrPath = join(appDir, '.next/server/chunks/ssr') + const ssrPath = join(appDir, '.next/server/chunks') const pageBundleBasenames = (await fs.readdir(ssrPath)).filter((p) => p.match(/\.js$/) ) diff --git a/test/integration/filesystempublicroutes/test/index.test.js b/test/integration/filesystempublicroutes/test/index.test.js index 428bb2ee52b0f..b887f37810d8b 100644 --- a/test/integration/filesystempublicroutes/test/index.test.js +++ b/test/integration/filesystempublicroutes/test/index.test.js @@ -40,7 +40,9 @@ describe('FileSystemPublicRoutes', () => { const res = await fetch('/exportpathmap-route') expect(res.status).toBe(200) const body = await res.text() - expect(body).toMatch(/exportpathmap was here/) + expect(body).toMatch( + process.env.TURBOPACK ? /turbopack/ : /exportpathmap was here/ + ) }) it('should still handle /_next routes', async () => { @@ -52,7 +54,9 @@ describe('FileSystemPublicRoutes', () => { const res = await fetch(join('/_next', pageFile)) expect(res.status).toBe(200) const body = await res.text() - expect(body).toMatch(/exportpathmap was here/) + expect(body).toMatch( + process.env.TURBOPACK ? /turbopack/ : /exportpathmap was here/ + ) }) it('should route to public folder files', async () => { diff --git a/test/integration/read-only-source-hmr/test/index.test.js b/test/integration/read-only-source-hmr/test/index.test.js index 26eef62eb382b..392c5ce1690a1 100644 --- a/test/integration/read-only-source-hmr/test/index.test.js +++ b/test/integration/read-only-source-hmr/test/index.test.js @@ -65,6 +65,11 @@ describe('Read-only source HMR', () => { const originalContent = await fs.readFile(pagePath, 'utf8') const editedContent = originalContent.replace('Hello World', 'COOL page') + if (process.env.TURBOPACK) { + // TODO Turbopack needs a bit to start watching + await new Promise((resolve) => setTimeout(resolve, 500)) + } + await writeReadOnlyFile(pagePath, editedContent) await check(() => getBrowserBodyText(browser), /COOL page/) @@ -86,8 +91,12 @@ describe('Read-only source HMR', () => { const originalContent = await fs.readFile(pagePath, 'utf8') - await fs.remove(pagePath) + if (process.env.TURBOPACK) { + // TODO Turbopack needs a bit to start watching + await new Promise((resolve) => setTimeout(resolve, 500)) + } + await fs.remove(pagePath) await writeReadOnlyFile(pagePath, originalContent) await check(() => getBrowserBodyText(browser), /Hello World/) } finally { @@ -106,7 +115,7 @@ describe('Read-only source HMR', () => { newPagePath, ` const New = () =>

New page

- + export default New ` ) diff --git a/test/integration/scss/test/basic-scss.test.js b/test/integration/scss/test/basic-scss.test.js index 92fa650467c0f..8fca08f7ae188 100644 --- a/test/integration/scss/test/basic-scss.test.js +++ b/test/integration/scss/test/basic-scss.test.js @@ -19,17 +19,16 @@ import { quote as shellQuote } from 'shell-quote' const fixturesDir = join(__dirname, '../..', 'scss-fixtures') describe('SCSS Support', () => { - describe('Friendly Webpack Error', () => { - const appDir = join(fixturesDir, 'webpack-error') + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + describe('Friendly Webpack Error', () => { + const appDir = join(fixturesDir, 'webpack-error') - const mockFile = join(appDir, 'mock.js') + const mockFile = join(appDir, 'mock.js') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - ;(process.env.TURBOPACK ? it.skip : it)( - 'should be a friendly error successfully', - async () => { + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + it('should be a friendly error successfully', async () => { const { code, stderr } = await nextBuild(appDir, [], { env: { NODE_OPTIONS: shellQuote([`--require`, mockFile]) }, stderr: true, @@ -52,186 +51,190 @@ describe('SCSS Support', () => { expect(stderr).not.toContain('css-loader') // eslint-disable-next-line expect(stderr).not.toContain('sass-loader') - } - ) - }) - - describe('CSS Compilation and Prefixing', () => { - const appDir = join(fixturesDir, 'compilation-and-prefixing') - - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - - it('should compile successfully', async () => { - const { code, stdout } = await nextBuild(appDir, [], { - stdout: true, }) - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) }) - it(`should've compiled and prefixed`, async () => { - const cssFolder = join(appDir, '.next/static/css') + describe('CSS Compilation and Prefixing', () => { + const appDir = join(fixturesDir, 'compilation-and-prefixing') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect( - cssContent.replace(/\/\*.*?\*\//g, '').trim() - ).toMatchInlineSnapshot( - `".redText ::placeholder{color:red}.flex-parsing{flex:0 0 calc(50% - var(--vertical-gutter))}"` - ) + it('should compile successfully', async () => { + const { code, stdout } = await nextBuild(appDir, [], { + stdout: true, + }) + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) - // Contains a source map - expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) - }) + it(`should've compiled and prefixed`, async () => { + const cssFolder = join(appDir, '.next/static/css') - it(`should've emitted a source map`, async () => { - const cssFolder = join(appDir, '.next/static/css') - - const files = await readdir(cssFolder) - const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f)) - - expect(cssMapFiles.length).toBe(1) - const cssMapContent = ( - await readFile(join(cssFolder, cssMapFiles[0]), 'utf8') - ).trim() - - const { version, mappings, sourcesContent } = JSON.parse(cssMapContent) - expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(` - { - "mappings": "AAEE,uBACE,SAHE,CAON,cACE,2CAAA", - "sourcesContent": [ - "$var: red; - .redText { - ::placeholder { - color: $var; - } - } + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - .flex-parsing { - flex: 0 0 calc(50% - var(--vertical-gutter)); - } - ", - ], - "version": 3, - } - `) - }) - }) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + `".redText ::placeholder{color:red}.flex-parsing{flex:0 0 calc(50% - var(--vertical-gutter))}"` + ) - describe('Can hot reload CSS without losing state', () => { - const appDir = join(fixturesDir, 'multi-page') + // Contains a source map + expect(cssContent).toMatch(/\/\*#\s*sourceMappingURL=(.+\.map)\s*\*\//) + }) - beforeAll(async () => { - await remove(join(appDir, '.next')) + it(`should've emitted a source map`, async () => { + const cssFolder = join(appDir, '.next/static/css') + + const files = await readdir(cssFolder) + const cssMapFiles = files.filter((f) => /\.css\.map$/.test(f)) + + expect(cssMapFiles.length).toBe(1) + const cssMapContent = ( + await readFile(join(cssFolder, cssMapFiles[0]), 'utf8') + ).trim() + + const { version, mappings, sourcesContent } = JSON.parse(cssMapContent) + expect({ version, mappings, sourcesContent }).toMatchInlineSnapshot(` +{ + "mappings": "AAEE,uBACE,SAHE,CAON,cACE,2CAAA", + "sourcesContent": [ + "$var: red; +.redText { + ::placeholder { + color: $var; + } +} + +.flex-parsing { + flex: 0 0 calc(50% - var(--vertical-gutter)); +} +", + ], + "version": 3, +} +`) + }) }) - let appPort - let app - beforeAll(async () => { - appPort = await findPort() - app = await launchApp(appDir, appPort) - }) - afterAll(async () => { - await killApp(app) - }) + describe('Has CSS in computed styles in Production', () => { + const appDir = join(fixturesDir, 'multi-page') + + let appPort + let app + let stdout + let code + beforeAll(async () => { + await remove(join(appDir, '.next')) + ;({ code, stdout } = await nextBuild(appDir, [], { + stdout: true, + })) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) - it('should update CSS color without remounting ', async () => { - let browser - try { - browser = await webdriver(appPort, '/page1') + it('should have compiled successfully', () => { + expect(code).toBe(0) + expect(stdout).toMatch(/Compiled successfully/) + }) - const desiredText = 'hello world' - await browser.elementById('text-input').type(desiredText) - expect(await browser.elementById('text-input').getValue()).toBe( - desiredText - ) + it('should have CSS for page', async () => { + const browser = await webdriver(appPort, '/page2') const currentColor = await browser.eval( - `window.getComputedStyle(document.querySelector('.red-text')).color` + `window.getComputedStyle(document.querySelector('.blue-text')).color` ) - expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) - - const cssFile = new File(join(appDir, 'styles/global1.scss')) - try { - cssFile.replace('$var: red', '$var: purple') - await waitFor(2000) // wait for HMR + expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`) + }) - const refreshedColor = await browser.eval( - `window.getComputedStyle(document.querySelector('.red-text')).color` - ) - expect(refreshedColor).toMatchInlineSnapshot(`"rgb(128, 0, 128)"`) + it(`should've preloaded the CSS file and injected it in `, async () => { + const content = await renderViaHTTP(appPort, '/page2') + const $ = cheerio.load(content) - // ensure text remained - expect(await browser.elementById('text-input').getValue()).toBe( - desiredText - ) - } finally { - cssFile.restore() - } - } finally { - if (browser) { - await browser.close() - } - } - }) - }) + const cssPreload = $('link[rel="preload"][as="style"]') + expect(cssPreload.length).toBe(1) + expect(cssPreload.attr('href')).toMatch( + /^\/_next\/static\/css\/.*\.css$/ + ) - describe('Has CSS in computed styles in Production', () => { - const appDir = join(fixturesDir, 'multi-page') - - let appPort - let app - let stdout - let code - beforeAll(async () => { - await remove(join(appDir, '.next')) - ;({ code, stdout } = await nextBuild(appDir, [], { - stdout: true, - })) - appPort = await findPort() - app = await nextStart(appDir, appPort) - }) - afterAll(async () => { - await killApp(app) - }) + const cssSheet = $('link[rel="stylesheet"]') + expect(cssSheet.length).toBe(1) + expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) - it('should have compiled successfully', () => { - expect(code).toBe(0) - expect(stdout).toMatch(/Compiled successfully/) + /* ensure CSS preloaded first */ + const allPreloads = [].slice.call($('link[rel="preload"]')) + const styleIndexes = allPreloads.flatMap((p, i) => + p.attribs.as === 'style' ? i : [] + ) + expect(styleIndexes).toEqual([0]) + }) }) + }) - it('should have CSS for page', async () => { - const browser = await webdriver(appPort, '/page2') + describe('development mode', () => { + describe('Can hot reload CSS without losing state', () => { + const appDir = join(fixturesDir, 'multi-page') - const currentColor = await browser.eval( - `window.getComputedStyle(document.querySelector('.blue-text')).color` - ) - expect(currentColor).toMatchInlineSnapshot(`"rgb(0, 0, 255)"`) - }) + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) - it(`should've preloaded the CSS file and injected it in `, async () => { - const content = await renderViaHTTP(appPort, '/page2') - const $ = cheerio.load(content) + let appPort + let app + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + }) - const cssPreload = $('link[rel="preload"][as="style"]') - expect(cssPreload.length).toBe(1) - expect(cssPreload.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + it('should update CSS color without remounting ', async () => { + let browser + try { + browser = await webdriver(appPort, '/page1') - const cssSheet = $('link[rel="stylesheet"]') - expect(cssSheet.length).toBe(1) - expect(cssSheet.attr('href')).toMatch(/^\/_next\/static\/css\/.*\.css$/) + const desiredText = 'hello world' + await browser.elementById('text-input').type(desiredText) + expect(await browser.elementById('text-input').getValue()).toBe( + desiredText + ) - /* ensure CSS preloaded first */ - const allPreloads = [].slice.call($('link[rel="preload"]')) - const styleIndexes = allPreloads.flatMap((p, i) => - p.attribs.as === 'style' ? i : [] - ) - expect(styleIndexes).toEqual([0]) + const currentColor = await browser.eval( + `window.getComputedStyle(document.querySelector('.red-text')).color` + ) + expect(currentColor).toMatchInlineSnapshot(`"rgb(255, 0, 0)"`) + + const cssFile = new File(join(appDir, 'styles/global1.scss')) + try { + cssFile.replace('$var: red', '$var: purple') + await waitFor(2000) // wait for HMR + + const refreshedColor = await browser.eval( + `window.getComputedStyle(document.querySelector('.red-text')).color` + ) + expect(refreshedColor).toMatchInlineSnapshot(`"rgb(128, 0, 128)"`) + + // ensure text remained + expect(await browser.elementById('text-input').getValue()).toBe( + desiredText + ) + } finally { + cssFile.restore() + } + } finally { + if (browser) { + await browser.close() + } + } + }) }) }) }) diff --git a/test/integration/scss/test/scss-loader-handling.test.js b/test/integration/scss/test/scss-loader-handling.test.js index 0c634cba6a899..faa819b50809c 100644 --- a/test/integration/scss/test/scss-loader-handling.test.js +++ b/test/integration/scss/test/scss-loader-handling.test.js @@ -7,15 +7,14 @@ import { join } from 'path' const fixturesDir = join(__dirname, '../..', 'scss-fixtures') describe('SCSS Support loader handling', () => { - describe('CSS URL via `file-loader`', () => { - const appDir = join(fixturesDir, 'url-global') - - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - ;(process.env.TURBOPACK ? it.skip : it)( - 'should compile successfully', - async () => { + ;(process.env.TURBOPACK ? describe.skip : describe)('production mode', () => { + describe('CSS URL via `file-loader`', () => { + const appDir = join(fixturesDir, 'url-global') + + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + it('should compile successfully', async () => { const { code, stdout } = await nextBuild(appDir, [], { stdout: true, }) @@ -23,49 +22,46 @@ describe('SCSS Support loader handling', () => { expect(code).toBe(0) // eslint-disable-next-line expect(stdout).toMatch(/Compiled successfully/) - } - ) - - it(`should've emitted expected files`, async () => { - const cssFolder = join(appDir, '.next/static/css') - const mediaFolder = join(appDir, '.next/static/media') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( - /^\.red-text\{color:red;background-image:url\(\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ - ) - - const mediaFiles = await readdir(mediaFolder) - expect(mediaFiles.length).toBe(3) - expect( - mediaFiles - .map((fileName) => - /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') - ) - .sort() - ).toMatchInlineSnapshot(` - [ - "dark.svg", - "dark2.svg", - "light.svg", - ] - `) + }) + + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') + const mediaFolder = join(appDir, '.next/static/media') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /^\.red-text\{color:red;background-image:url\(\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ + ) + + const mediaFiles = await readdir(mediaFolder) + expect(mediaFiles.length).toBe(3) + expect( + mediaFiles + .map((fileName) => + /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') + ) + .sort() + ).toMatchInlineSnapshot(` + [ + "dark.svg", + "dark2.svg", + "light.svg", + ] + `) + }) }) - }) - describe('CSS URL via file-loader sass partial', () => { - const appDir = join(fixturesDir, 'url-global-partial') + describe('CSS URL via file-loader sass partial', () => { + const appDir = join(fixturesDir, 'url-global-partial') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - ;(process.env.TURBOPACK ? it.skip : it)( - 'should compile successfully', - async () => { + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + it('should compile successfully', async () => { const { code, stdout } = await nextBuild(appDir, [], { stdout: true, }) @@ -73,51 +69,48 @@ describe('SCSS Support loader handling', () => { expect(code).toBe(0) // eslint-disable-next-line expect(stdout).toMatch(/Compiled successfully/) - } - ) - - it(`should've emitted expected files`, async () => { - const cssFolder = join(appDir, '.next/static/css') - const mediaFolder = join(appDir, '.next/static/media') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect( - cssContent.replace(/\/\*.*?\*\//g, '').trim() - ).toMatchInlineSnapshot( - '".red-text{color:red;background-image:url(/_next/static/media/darka.6b01655b.svg),url(/_next/static/media/darkb.6b01655b.svg)}.blue-text{color:orange;font-weight:bolder;background-image:url(/_next/static/media/light.2da1d3d6.svg);color:blue}"' - ) - - const mediaFiles = await readdir(mediaFolder) - expect(mediaFiles.length).toBe(3) - expect( - mediaFiles - .map((fileName) => - /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') - ) - .sort() - ).toMatchInlineSnapshot(` - [ - "darka.svg", - "darkb.svg", - "light.svg", - ] - `) + }) + + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') + const mediaFolder = join(appDir, '.next/static/media') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect( + cssContent.replace(/\/\*.*?\*\//g, '').trim() + ).toMatchInlineSnapshot( + '".red-text{color:red;background-image:url(/_next/static/media/darka.6b01655b.svg),url(/_next/static/media/darkb.6b01655b.svg)}.blue-text{color:orange;font-weight:bolder;background-image:url(/_next/static/media/light.2da1d3d6.svg);color:blue}"' + ) + + const mediaFiles = await readdir(mediaFolder) + expect(mediaFiles.length).toBe(3) + expect( + mediaFiles + .map((fileName) => + /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') + ) + .sort() + ).toMatchInlineSnapshot(` + [ + "darka.svg", + "darkb.svg", + "light.svg", + ] + `) + }) }) - }) - describe('CSS URL via `file-loader` and asset prefix (1)', () => { - const appDir = join(fixturesDir, 'url-global-asset-prefix-1') + describe('CSS URL via `file-loader` and asset prefix (1)', () => { + const appDir = join(fixturesDir, 'url-global-asset-prefix-1') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - ;(process.env.TURBOPACK ? it.skip : it)( - 'should compile successfully', - async () => { + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + it('should compile successfully', async () => { const { code, stdout } = await nextBuild(appDir, [], { stdout: true, }) @@ -125,49 +118,46 @@ describe('SCSS Support loader handling', () => { expect(code).toBe(0) // eslint-disable-next-line expect(stdout).toMatch(/Compiled successfully/) - } - ) - - it(`should've emitted expected files`, async () => { - const cssFolder = join(appDir, '.next/static/css') - const mediaFolder = join(appDir, '.next/static/media') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( - /^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ - ) - - const mediaFiles = await readdir(mediaFolder) - expect(mediaFiles.length).toBe(3) - expect( - mediaFiles - .map((fileName) => - /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') - ) - .sort() - ).toMatchInlineSnapshot(` - [ - "dark.svg", - "dark2.svg", - "light.svg", - ] - `) + }) + + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') + const mediaFolder = join(appDir, '.next/static/media') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ + ) + + const mediaFiles = await readdir(mediaFolder) + expect(mediaFiles.length).toBe(3) + expect( + mediaFiles + .map((fileName) => + /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') + ) + .sort() + ).toMatchInlineSnapshot(` + [ + "dark.svg", + "dark2.svg", + "light.svg", + ] + `) + }) }) - }) - describe('CSS URL via `file-loader` and asset prefix (2)', () => { - const appDir = join(fixturesDir, 'url-global-asset-prefix-2') + describe('CSS URL via `file-loader` and asset prefix (2)', () => { + const appDir = join(fixturesDir, 'url-global-asset-prefix-2') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - ;(process.env.TURBOPACK ? it.skip : it)( - 'should compile successfully', - async () => { + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + it('should compile successfully', async () => { const { code, stdout } = await nextBuild(appDir, [], { stdout: true, }) @@ -175,49 +165,46 @@ describe('SCSS Support loader handling', () => { expect(code).toBe(0) // eslint-disable-next-line expect(stdout).toMatch(/Compiled successfully/) - } - ) - - it(`should've emitted expected files`, async () => { - const cssFolder = join(appDir, '.next/static/css') - const mediaFolder = join(appDir, '.next/static/media') - - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) - - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( - /^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ - ) - - const mediaFiles = await readdir(mediaFolder) - expect(mediaFiles.length).toBe(3) - expect( - mediaFiles - .map((fileName) => - /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') - ) - .sort() - ).toMatchInlineSnapshot(` - [ - "dark.svg", - "dark2.svg", - "light.svg", - ] - `) + }) + + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') + const mediaFolder = join(appDir, '.next/static/media') + + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) + + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /^\.red-text\{color:red;background-image:url\(\/foo\/_next\/static\/media\/dark\.[a-f0-9]{8}\.svg\) url\(\/foo\/_next\/static\/media\/dark2\.[a-f0-9]{8}\.svg\)\}\.blue-text\{color:orange;font-weight:bolder;background-image:url\(\/foo\/_next\/static\/media\/light\.[a-f0-9]{8}\.svg\);color:blue\}$/ + ) + + const mediaFiles = await readdir(mediaFolder) + expect(mediaFiles.length).toBe(3) + expect( + mediaFiles + .map((fileName) => + /^(.+?)\..{8}\.(.+?)$/.exec(fileName).slice(1).join('.') + ) + .sort() + ).toMatchInlineSnapshot(` + [ + "dark.svg", + "dark2.svg", + "light.svg", + ] + `) + }) }) - }) - describe('Data Urls', () => { - const appDir = join(fixturesDir, 'data-url') + describe('Data Urls', () => { + const appDir = join(fixturesDir, 'data-url') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - ;(process.env.TURBOPACK ? it.skip : it)( - 'should compile successfully', - async () => { + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + it('should compile successfully', async () => { const { code, stdout } = await nextBuild(appDir, [], { stdout: true, }) @@ -225,32 +212,29 @@ describe('SCSS Support loader handling', () => { expect(code).toBe(0) // eslint-disable-next-line expect(stdout).toMatch(/Compiled successfully/) - } - ) + }) - it(`should've emitted expected files`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( - /^\.red-text\{color:red;background-image:url\("data:[^"]+"\)\}$/ - ) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /^\.red-text\{color:red;background-image:url\("data:[^"]+"\)\}$/ + ) + }) }) - }) - describe('External imports', () => { - const appDir = join(fixturesDir, 'external-url') + describe('External imports', () => { + const appDir = join(fixturesDir, 'external-url') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - ;(process.env.TURBOPACK ? it.skip : it)( - 'should compile successfully', - async () => { + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + it('should compile successfully', async () => { const { code, stdout } = await nextBuild(appDir, [], { stdout: true, }) @@ -258,38 +242,35 @@ describe('SCSS Support loader handling', () => { expect(code).toBe(0) // eslint-disable-next-line expect(stdout).toMatch(/Compiled successfully/) - } - ) + }) - it(`should've emitted expected files`, async () => { - const cssFolder = join(appDir, '.next/static/css') + it(`should've emitted expected files`, async () => { + const cssFolder = join(appDir, '.next/static/css') - const files = await readdir(cssFolder) - const cssFiles = files.filter((f) => /\.css$/.test(f)) + const files = await readdir(cssFolder) + const cssFiles = files.filter((f) => /\.css$/.test(f)) - expect(cssFiles.length).toBe(1) - const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') - expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( - /^@import"https:\/\/fonts\.googleapis\.com\/css2\?family=Poppins:wght@400&display=swap";\.red-text\{color:red;font-family:Poppins;font-style:normal;font-weight:400\}$/ - ) + expect(cssFiles.length).toBe(1) + const cssContent = await readFile(join(cssFolder, cssFiles[0]), 'utf8') + expect(cssContent.replace(/\/\*.*?\*\//g, '').trim()).toMatch( + /^@import"https:\/\/fonts\.googleapis\.com\/css2\?family=Poppins:wght@400&display=swap";\.red-text\{color:red;font-family:Poppins;font-style:normal;font-weight:400\}$/ + ) + }) }) - }) - describe('Preprocessor loader order', () => { - const appDir = join(fixturesDir, 'loader-order') + describe('Preprocessor loader order', () => { + const appDir = join(fixturesDir, 'loader-order') - beforeAll(async () => { - await remove(join(appDir, '.next')) - }) - ;(process.env.TURBOPACK ? it.skip : it)( - 'should compile successfully', - async () => { + beforeAll(async () => { + await remove(join(appDir, '.next')) + }) + it('should compile successfully', async () => { const { stdout } = await nextBuild(appDir, [], { stdout: true, }) // eslint-disable-next-line expect(stdout).toMatch(/Compiled successfully/) - } - ) + }) + }) }) })