diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml
index 6c43b333a5843..20cf54fb8bf28 100644
--- a/.github/workflows/build_test_deploy.yml
+++ b/.github/workflows/build_test_deploy.yml
@@ -1072,19 +1072,6 @@ jobs:
if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }}
run: node scripts/normalize-version-bump.js
- # We use restore-key to pick latest cache.
- # We will not get exact match, but doc says
- # "If there are multiple partial matches for a restore key, the action returns the most recently created cache."
- # So we get latest cache
- # - name: Cache built files
- # uses: actions/cache@v3
- # timeout-minutes: 5
- # with:
- # path: ./packages/next-swc/target
- # key: next-swc-cargo-cache-dev-ubuntu-latest-${{ hashFiles('**/Cargo.lock') }}
- # restore-keys: |
- # next-swc-cargo-cache-dev-ubuntu-latest
-
- name: Build in docker
uses: addnab/docker-run-action@v3
if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }}
@@ -1327,18 +1314,6 @@ jobs:
# we use checkout here instead of the build cache since
# it can fail to restore in different OS'
- uses: actions/checkout@v3
- # We use restore-key to pick latest cache.
- # We will not get exact match, but doc says
- # "If there are multiple partial matches for a restore key, the action returns the most recently created cache."
- # So we get latest cache
- - name: Cache built files
- uses: actions/cache@v3
- timeout-minutes: 5
- with:
- path: ./packages/next-swc/target
- key: next-swc-cargo-cache-${{ matrix.settings.target }}--${{ hashFiles('**/Cargo.lock') }}
- restore-keys: |
- next-swc-cargo-cache-${{ matrix.settings.target }}
- name: Setup node
uses: actions/setup-node@v3
diff --git a/docs/api-reference/next/image.md b/docs/api-reference/next/image.md
index 3174c42aced7b..f9418a3eea9b7 100644
--- a/docs/api-reference/next/image.md
+++ b/docs/api-reference/next/image.md
@@ -16,6 +16,7 @@ description: Enable Image Optimization with the built-in Image component.
| Version | Changes |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `v13.2.0` | `contentDispositionType` configuration added. |
| `v13.0.6` | `ref` prop added. |
| `v13.0.0` | `` wrapper removed. `layout`, `objectFit`, `objectPosition`, `lazyBoundary`, `lazyRoot` props removed. `alt` is required. `onLoadingComplete` receives reference to `img` element. Built-in loader config removed. |
| `v12.3.0` | `remotePatterns` and `unoptimized` configuration is stable. |
@@ -503,17 +504,20 @@ module.exports = {
The default [loader](#loader) does not optimize SVG images for a few reasons. First, SVG is a vector format meaning it can be resized losslessly. Second, SVG has many of the same features as HTML/CSS, which can lead to vulnerabilities without proper [Content Security Policy (CSP) headers](/docs/advanced-features/security-headers.md).
-If you need to serve SVG images with the default Image Optimization API, you can set `dangerouslyAllowSVG` and `contentSecurityPolicy` inside your `next.config.js`:
+If you need to serve SVG images with the default Image Optimization API, you can set `dangerouslyAllowSVG` inside your `next.config.js`:
```js
module.exports = {
images: {
dangerouslyAllowSVG: true,
+ contentDispositionType: 'attachment',
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
},
}
```
+In addition, it is strongly recommended to also set `contentDispositionType` to force the browser to download the image, as well as `contentSecurityPolicy` to prevent scripts embedded in the image from executing.
+
### Animated Images
The default [loader](#loader) will automatically bypass Image Optimization for animated images and serve the image as-is.
diff --git a/docs/api-reference/next/legacy/image.md b/docs/api-reference/next/legacy/image.md
index c6b8176c4d169..f5ef5d2468b13 100644
--- a/docs/api-reference/next/legacy/image.md
+++ b/docs/api-reference/next/legacy/image.md
@@ -571,17 +571,20 @@ module.exports = {
The default [loader](#loader) does not optimize SVG images for a few reasons. First, SVG is a vector format meaning it can be resized losslessly. Second, SVG has many of the same features as HTML/CSS, which can lead to vulnerabilities without proper [Content Security Policy (CSP) headers](/docs/advanced-features/security-headers.md).
-If you need to serve SVG images with the default Image Optimization API, you can set `dangerouslyAllowSVG` and `contentSecurityPolicy` inside your `next.config.js`:
+If you need to serve SVG images with the default Image Optimization API, you can set `dangerouslyAllowSVG` inside your `next.config.js`:
```js
module.exports = {
images: {
dangerouslyAllowSVG: true,
+ contentDispositionType: 'attachment',
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
},
}
```
+In addition, it is strongly recommended to also set `contentDispositionType` to force the browser to download the image, as well as `contentSecurityPolicy` to prevent scripts embedded in the image from executing.
+
### Animated Images
The default [loader](#loader) will automatically bypass Image Optimization for animated images and serve the image as-is.
diff --git a/errors/invalid-images-config.md b/errors/invalid-images-config.md
index badaa4f3e1a88..d76741e61f3d9 100644
--- a/errors/invalid-images-config.md
+++ b/errors/invalid-images-config.md
@@ -33,6 +33,8 @@ module.exports = {
dangerouslyAllowSVG: false,
// set the Content-Security-Policy header
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
+ // sets the Content-Disposition header (inline or attachment)
+ contentDispositionType: 'inline',
// limit of 50 objects
remotePatterns: [],
// when true, every image will be unoptimized
diff --git a/packages/next-swc/Cargo.lock b/packages/next-swc/Cargo.lock
index 3754d2e472572..b35a3d4a268a6 100644
--- a/packages/next-swc/Cargo.lock
+++ b/packages/next-swc/Cargo.lock
@@ -126,7 +126,7 @@ dependencies = [
[[package]]
name = "auto-hash-map"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"serde",
]
@@ -2285,7 +2285,7 @@ checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
name = "next-binding"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"mdxjs",
"modularize_imports",
@@ -2301,7 +2301,7 @@ dependencies = [
[[package]]
name = "next-core"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"auto-hash-map",
@@ -2331,7 +2331,7 @@ dependencies = [
[[package]]
name = "next-dev"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"dunce",
@@ -2357,7 +2357,7 @@ dependencies = [
[[package]]
name = "next-font"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"fxhash",
"serde",
@@ -2411,7 +2411,7 @@ dependencies = [
[[package]]
name = "next-transform-dynamic"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"pathdiff",
"swc_core",
@@ -2420,7 +2420,7 @@ dependencies = [
[[package]]
name = "next-transform-strip-page-exports"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"fxhash",
"swc_core",
@@ -2430,7 +2430,7 @@ dependencies = [
[[package]]
name = "node-file-trace"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"clap",
@@ -5458,7 +5458,7 @@ dependencies = [
[[package]]
name = "turbo-malloc"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"mimalloc",
]
@@ -5466,7 +5466,7 @@ dependencies = [
[[package]]
name = "turbo-tasks"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"auto-hash-map",
@@ -5496,7 +5496,7 @@ dependencies = [
[[package]]
name = "turbo-tasks-build"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"cargo-lock",
@@ -5508,7 +5508,7 @@ dependencies = [
[[package]]
name = "turbo-tasks-env"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"dotenvy",
@@ -5522,7 +5522,7 @@ dependencies = [
[[package]]
name = "turbo-tasks-fetch"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"indexmap",
@@ -5539,7 +5539,7 @@ dependencies = [
[[package]]
name = "turbo-tasks-fs"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"auto-hash-map",
@@ -5566,7 +5566,7 @@ dependencies = [
[[package]]
name = "turbo-tasks-hash"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"base16",
"hex",
@@ -5578,7 +5578,7 @@ dependencies = [
[[package]]
name = "turbo-tasks-macros"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"convert_case 0.5.0",
@@ -5592,7 +5592,7 @@ dependencies = [
[[package]]
name = "turbo-tasks-macros-shared"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"proc-macro2",
"quote",
@@ -5602,7 +5602,7 @@ dependencies = [
[[package]]
name = "turbo-tasks-memory"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"auto-hash-map",
@@ -5624,7 +5624,7 @@ dependencies = [
[[package]]
name = "turbopack"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"indexmap",
@@ -5649,7 +5649,7 @@ dependencies = [
[[package]]
name = "turbopack-cli-utils"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"clap",
@@ -5665,7 +5665,7 @@ dependencies = [
[[package]]
name = "turbopack-core"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"async-trait",
@@ -5691,7 +5691,7 @@ dependencies = [
[[package]]
name = "turbopack-css"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"async-trait",
@@ -5713,7 +5713,7 @@ dependencies = [
[[package]]
name = "turbopack-dev-server"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"futures",
@@ -5743,7 +5743,7 @@ dependencies = [
[[package]]
name = "turbopack-ecmascript"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"async-trait",
@@ -5782,7 +5782,7 @@ dependencies = [
[[package]]
name = "turbopack-env"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"serde",
@@ -5797,7 +5797,7 @@ dependencies = [
[[package]]
name = "turbopack-json"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"serde",
@@ -5812,7 +5812,7 @@ dependencies = [
[[package]]
name = "turbopack-mdx"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"mdxjs",
@@ -5827,7 +5827,7 @@ dependencies = [
[[package]]
name = "turbopack-node"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"futures",
@@ -5840,6 +5840,7 @@ dependencies = [
"tokio",
"turbo-tasks",
"turbo-tasks-build",
+ "turbo-tasks-env",
"turbo-tasks-fs",
"turbopack-core",
"turbopack-dev-server",
@@ -5850,7 +5851,7 @@ dependencies = [
[[package]]
name = "turbopack-static"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"anyhow",
"serde",
@@ -5866,7 +5867,7 @@ dependencies = [
[[package]]
name = "turbopack-swc-utils"
version = "0.1.0"
-source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230221.3#8fcf5d1123df0e00e381d13c8fc2e2b3e33131a8"
+source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230222.2#9942db86699dc191f6572a619daf3a5e23732a22"
dependencies = [
"swc_core",
"turbo-tasks",
diff --git a/packages/next-swc/crates/core/Cargo.toml b/packages/next-swc/crates/core/Cargo.toml
index 21c927b29e97d..dec593494c049 100644
--- a/packages/next-swc/crates/core/Cargo.toml
+++ b/packages/next-swc/crates/core/Cargo.toml
@@ -19,7 +19,7 @@ serde = "1"
serde_json = "1"
tracing = { version = "0.1.37", features = ["release_max_level_info"] }
-next-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230221.3", features = [
+next-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230222.2", features = [
"__swc_core",
"__swc_core_next_core",
"__swc_transform_styled_jsx",
@@ -29,7 +29,7 @@ next-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-2
] }
[dev-dependencies]
-next-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230221.3", features = [
+next-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230222.2", features = [
"__swc_core_testing_transform",
"__swc_testing",
] }
diff --git a/packages/next-swc/crates/napi/Cargo.toml b/packages/next-swc/crates/napi/Cargo.toml
index a60d20fec26e1..f0128da1ec914 100644
--- a/packages/next-swc/crates/napi/Cargo.toml
+++ b/packages/next-swc/crates/napi/Cargo.toml
@@ -39,10 +39,10 @@ tracing = { version = "0.1.37", features = ["release_max_level_info"] }
tracing-futures = "0.2.5"
tracing-subscriber = "0.3.9"
tracing-chrome = "0.5.0"
-turbo-malloc = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230221.3" }
-turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230221.3" }
-turbo-tasks-memory = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230221.3" }
-next-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230221.3", features = [
+turbo-malloc = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230222.2" }
+turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230222.2" }
+turbo-tasks-memory = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230222.2" }
+next-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230222.2", features = [
"__swc_core_binding_napi",
"__turbo_next_dev_server",
"__turbo_node_file_trace",
diff --git a/packages/next-swc/crates/wasm/Cargo.toml b/packages/next-swc/crates/wasm/Cargo.toml
index a3c8d28e2fb1b..b13e9b825fabd 100644
--- a/packages/next-swc/crates/wasm/Cargo.toml
+++ b/packages/next-swc/crates/wasm/Cargo.toml
@@ -31,7 +31,7 @@ wasm-bindgen-futures = "0.4.8"
getrandom = { version = "0.2.5", optional = true, default-features = false }
js-sys = "0.3.59"
serde-wasm-bindgen = "0.4.3"
-next-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230221.3", features = [
+next-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230222.2", features = [
"__swc_core_binding_wasm",
"__feature_mdx_rs",
] }
diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/Terminal/Terminal.tsx b/packages/next/src/client/components/react-dev-overlay/internal/components/Terminal/Terminal.tsx
index ed61d5fd13e3a..e563d69b26c6c 100644
--- a/packages/next/src/client/components/react-dev-overlay/internal/components/Terminal/Terminal.tsx
+++ b/packages/next/src/client/components/react-dev-overlay/internal/components/Terminal/Terminal.tsx
@@ -1,5 +1,6 @@
import Anser from 'next/dist/compiled/anser'
import * as React from 'react'
+import { HotlinkedText } from '../hot-linked-text'
import { EditorLink } from './EditorLink'
export type TerminalProps = { content: string }
@@ -59,7 +60,7 @@ export const Terminal: React.FC = function Terminal({
: undefined),
}}
>
- {entry.content}
+
))}
{editorLinks.map((file) => (
diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/Terminal/styles.tsx b/packages/next/src/client/components/react-dev-overlay/internal/components/Terminal/styles.tsx
index f9bee2f8c5763..bf96e64248fa6 100644
--- a/packages/next/src/client/components/react-dev-overlay/internal/components/Terminal/styles.tsx
+++ b/packages/next/src/client/components/react-dev-overlay/internal/components/Terminal/styles.tsx
@@ -40,6 +40,9 @@ const styles = css`
[data-with-open-in-editor-link] {
margin-left: var(--size-gap-double);
}
+ [data-nextjs-terminal] a {
+ color: inherit;
+ }
`
export { styles }
diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/hot-linked-text/get-words-and-whitespaces.test.ts b/packages/next/src/client/components/react-dev-overlay/internal/components/hot-linked-text/get-words-and-whitespaces.test.ts
new file mode 100644
index 0000000000000..9105ff91c8fc8
--- /dev/null
+++ b/packages/next/src/client/components/react-dev-overlay/internal/components/hot-linked-text/get-words-and-whitespaces.test.ts
@@ -0,0 +1,17 @@
+import { getWordsAndWhitespaces } from './get-words-and-whitespaces'
+
+describe('getWordsAndWhitespaces', () => {
+ it('should return sequences of words and whitespaces', () => {
+ const text = ' \n\nhello world https://nextjs.org/\nhttps://nextjs.org/'
+ expect(getWordsAndWhitespaces(text)).toEqual([
+ ' \n\n',
+ 'hello',
+ ' ',
+ 'world',
+ ' ',
+ 'https://nextjs.org/',
+ '\n',
+ 'https://nextjs.org/',
+ ])
+ })
+})
diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/hot-linked-text/get-words-and-whitespaces.ts b/packages/next/src/client/components/react-dev-overlay/internal/components/hot-linked-text/get-words-and-whitespaces.ts
new file mode 100644
index 0000000000000..a1632bdb12dbe
--- /dev/null
+++ b/packages/next/src/client/components/react-dev-overlay/internal/components/hot-linked-text/get-words-and-whitespaces.ts
@@ -0,0 +1,37 @@
+function isWhitespace(char: string) {
+ return char === ' ' || char === '\n'
+}
+
+/**
+ * Get sequences of words and whitespaces from a string.
+ *
+ * e.g. "Hello world \n\n" -> ["Hello", " ", "world", " \n\n"]
+ */
+export function getWordsAndWhitespaces(text: string) {
+ const wordsAndWhitespaces: string[] = []
+
+ let current = ''
+ let currentIsWhitespace = false
+ for (const char of text) {
+ if (current.length === 0) {
+ current += char
+ currentIsWhitespace = isWhitespace(char)
+ continue
+ }
+
+ const nextIsWhitespace = isWhitespace(char)
+ if (currentIsWhitespace === nextIsWhitespace) {
+ current += char
+ } else {
+ wordsAndWhitespaces.push(current)
+ current = char
+ currentIsWhitespace = nextIsWhitespace
+ }
+ }
+
+ if (current.length > 0) {
+ wordsAndWhitespaces.push(current)
+ }
+
+ return wordsAndWhitespaces
+}
diff --git a/packages/next/src/client/components/react-dev-overlay/internal/components/hot-linked-text/index.tsx b/packages/next/src/client/components/react-dev-overlay/internal/components/hot-linked-text/index.tsx
new file mode 100644
index 0000000000000..2dde5aba96952
--- /dev/null
+++ b/packages/next/src/client/components/react-dev-overlay/internal/components/hot-linked-text/index.tsx
@@ -0,0 +1,29 @@
+import React from 'react'
+import { getWordsAndWhitespaces } from './get-words-and-whitespaces'
+
+const linkRegex = /https?:\/\/[^\s/$.?#].[^\s"]*/i
+
+export const HotlinkedText: React.FC<{
+ text: string
+}> = function HotlinkedText(props) {
+ const { text } = props
+
+ const wordsAndWhitespaces = getWordsAndWhitespaces(text)
+
+ return (
+ <>
+ {linkRegex.test(text)
+ ? wordsAndWhitespaces.map((word, index) => {
+ if (linkRegex.test(word)) {
+ return (
+
+ {word}
+
+ )
+ }
+ return {word}
+ })
+ : text}
+ >
+ )
+}
diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx
index 5a2cc24c5137c..0714bdd55b5ba 100644
--- a/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx
+++ b/packages/next/src/client/components/react-dev-overlay/internal/container/Errors.tsx
@@ -21,6 +21,7 @@ import { CloseIcon } from '../icons/CloseIcon'
import { RuntimeError } from './RuntimeError'
import { VersionStalenessInfo } from '../components/VersionStalenessInfo'
import type { VersionInfo } from '../../../../../server/dev/parse-version-info'
+import { HotlinkedText } from '../components/hot-linked-text'
export type SupportedErrorEvent = {
id: number
@@ -52,35 +53,6 @@ function getErrorSignature(ev: SupportedErrorEvent): string {
return ''
}
-const HotlinkedText: React.FC<{
- text: string
-}> = function HotlinkedText(props) {
- const { text } = props
-
- const linkRegex = /https?:\/\/[^\s/$.?#].[^\s"]*/i
- return (
- <>
- {linkRegex.test(text)
- ? text.split(' ').map((word, index, array) => {
- if (linkRegex.test(word)) {
- return (
-
- {word}
- {index === array.length - 1 ? '' : ' '}
-
- )
- }
- return index === array.length - 1 ? (
- {word}
- ) : (
- {word}
- )
- })
- : text}
- >
- )
-}
-
export const Errors: React.FC = function Errors({
errors,
initialDisplayState,
diff --git a/packages/next/src/server/config-schema.ts b/packages/next/src/server/config-schema.ts
index 10f6280dd57ff..d4b262fc9bc27 100644
--- a/packages/next/src/server/config-schema.ts
+++ b/packages/next/src/server/config-schema.ts
@@ -598,6 +598,10 @@ const configSchema = {
minLength: 1,
type: 'string',
},
+ contentDispositionType: {
+ enum: ['inline', 'attachment'] as any, // automatic typing does not like enum
+ type: 'string',
+ },
dangerouslyAllowSVG: {
type: 'boolean',
},
diff --git a/packages/next/src/server/image-optimizer.ts b/packages/next/src/server/image-optimizer.ts
index 6f881fbb7eeef..f0fa31bd6d579 100644
--- a/packages/next/src/server/image-optimizer.ts
+++ b/packages/next/src/server/image-optimizer.ts
@@ -23,6 +23,7 @@ import { IncrementalCacheEntry, IncrementalCacheValue } from './response-cache'
import { mockRequest } from './lib/mock-request'
import { hasMatch } from '../shared/lib/match-remote-pattern'
import { getImageBlurSvg } from '../shared/lib/image-blur-svg'
+import { ImageConfigComplete } from '../shared/lib/image-config'
type XCacheHeader = 'MISS' | 'HIT' | 'STALE'
@@ -672,11 +673,11 @@ export async function imageOptimizer(
function getFileNameWithExtension(
url: string,
contentType: string | null
-): string | void {
+): string {
const [urlWithoutQueryParams] = url.split('?')
const fileNameWithExtension = urlWithoutQueryParams.split('/').pop()
if (!contentType || !fileNameWithExtension) {
- return
+ return 'image.bin'
}
const [fileName] = fileNameWithExtension.split('.')
@@ -692,7 +693,7 @@ function setResponseHeaders(
contentType: string | null,
isStatic: boolean,
xCache: XCacheHeader,
- contentSecurityPolicy: string,
+ imagesConfig: ImageConfigComplete,
maxAge: number,
isDev: boolean
) {
@@ -712,16 +713,12 @@ function setResponseHeaders(
}
const fileName = getFileNameWithExtension(url, contentType)
- if (fileName) {
- res.setHeader(
- 'Content-Disposition',
- contentDisposition(fileName, { type: 'inline' })
- )
- }
+ res.setHeader(
+ 'Content-Disposition',
+ contentDisposition(fileName, { type: imagesConfig.contentDispositionType })
+ )
- if (contentSecurityPolicy) {
- res.setHeader('Content-Security-Policy', contentSecurityPolicy)
- }
+ res.setHeader('Content-Security-Policy', imagesConfig.contentSecurityPolicy)
res.setHeader('X-Nextjs-Cache', xCache)
return { finished: false }
@@ -735,7 +732,7 @@ export function sendResponse(
buffer: Buffer,
isStatic: boolean,
xCache: XCacheHeader,
- contentSecurityPolicy: string,
+ imagesConfig: ImageConfigComplete,
maxAge: number,
isDev: boolean
) {
@@ -749,7 +746,7 @@ export function sendResponse(
contentType,
isStatic,
xCache,
- contentSecurityPolicy,
+ imagesConfig,
maxAge,
isDev
)
diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts
index 6f5dec54a3f18..34808e1ceb7f5 100644
--- a/packages/next/src/server/next-server.ts
+++ b/packages/next/src/server/next-server.ts
@@ -466,7 +466,7 @@ export default class NextNodeServer extends BaseServer {
cacheEntry.value.buffer,
paramsResult.isStatic,
cacheEntry.isMiss ? 'MISS' : cacheEntry.isStale ? 'STALE' : 'HIT',
- imagesConfig.contentSecurityPolicy,
+ imagesConfig,
cacheEntry.revalidate || 0,
Boolean(this.renderOpts.dev)
)
diff --git a/packages/next/src/shared/lib/image-config.ts b/packages/next/src/shared/lib/image-config.ts
index e13cdb6943712..34566883ab647 100644
--- a/packages/next/src/shared/lib/image-config.ts
+++ b/packages/next/src/shared/lib/image-config.ts
@@ -88,6 +88,9 @@ export type ImageConfigComplete = {
/** @see [Dangerously Allow SVG](https://nextjs.org/docs/api-reference/next/image#dangerously-allow-svg) */
contentSecurityPolicy: string
+ /** @see [Dangerously Allow SVG](https://nextjs.org/docs/api-reference/next/image#dangerously-allow-svg) */
+ contentDispositionType: 'inline' | 'attachment'
+
/** @see [Remote Patterns](https://nextjs.org/docs/api-reference/next/image#remote-patterns) */
remotePatterns: RemotePattern[]
@@ -109,6 +112,7 @@ export const imageConfigDefault: ImageConfigComplete = {
formats: ['image/webp'],
dangerouslyAllowSVG: false,
contentSecurityPolicy: `script-src 'none'; frame-src 'none'; sandbox;`,
+ contentDispositionType: 'inline',
remotePatterns: [],
unoptimized: false,
}
diff --git a/test/development/acceptance-app/error-message-url.test.ts b/test/development/acceptance-app/error-message-url.test.ts
new file mode 100644
index 0000000000000..138c102746c40
--- /dev/null
+++ b/test/development/acceptance-app/error-message-url.test.ts
@@ -0,0 +1,73 @@
+import { createNextDescribe, FileRef } from 'e2e-utils'
+import path from 'path'
+import { sandbox } from './helpers'
+
+createNextDescribe(
+ 'Error overlay - error message urls',
+ {
+ files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')),
+ dependencies: {
+ react: 'latest',
+ 'react-dom': 'latest',
+ },
+ skipStart: true,
+ },
+ ({ next }) => {
+ it('should be possible to click url in build error', async () => {
+ const { session, browser, cleanup } = await sandbox(next)
+
+ const content = await next.readFile('app/page.js')
+
+ await session.patch(
+ 'app/page.js',
+ content + '\nexport function getServerSideProps() {}'
+ )
+
+ expect(await session.hasRedbox(true)).toBe(true)
+
+ const link = await browser.elementByCss('[data-nextjs-terminal] a')
+ const text = await link.text()
+ const href = await link.getAttribute('href')
+ expect(text).toEqual(
+ 'https://beta.nextjs.org/docs/data-fetching/fundamentals'
+ )
+ expect(href).toEqual(
+ 'https://beta.nextjs.org/docs/data-fetching/fundamentals'
+ )
+
+ await cleanup()
+ })
+
+ it('should be possible to click url in runtime error', async () => {
+ const { session, browser, cleanup } = await sandbox(
+ next,
+ new Map([
+ [
+ 'app/page.js',
+ `'use client'
+ export default function Page() {
+ return typeof window === 'undefined' ? 'HELLO' : 'WORLD'
+ }
+ `,
+ ],
+ ])
+ )
+
+ await session.waitForAndOpenRuntimeError()
+
+ const link = await browser.elementByCss(
+ '#nextjs__container_errors_desc a'
+ )
+ const text = await link.text()
+ const href = await link.getAttribute('href')
+ expect(text).toEqual(
+ 'https://nextjs.org/docs/messages/react-hydration-error'
+ )
+ expect(href).toEqual(
+ 'https://nextjs.org/docs/messages/react-hydration-error'
+ )
+
+ await cleanup()
+ })
+ }
+)
diff --git a/test/e2e/app-dir/metadata/app/async/layout.tsx b/test/e2e/app-dir/metadata/app/async/layout.tsx
index 0c87e2431254e..8168b5728b5d7 100644
--- a/test/e2e/app-dir/metadata/app/async/layout.tsx
+++ b/test/e2e/app-dir/metadata/app/async/layout.tsx
@@ -7,3 +7,5 @@ export async function generateMetadata() {
keywords: 'parent',
}
}
+
+export const revalidate = 0
diff --git a/test/e2e/app-dir/metadata/app/async/not-found/page.tsx b/test/e2e/app-dir/metadata/app/async/not-found/page.tsx
index a47fce01d4007..2c47965dbeca1 100644
--- a/test/e2e/app-dir/metadata/app/async/not-found/page.tsx
+++ b/test/e2e/app-dir/metadata/app/async/not-found/page.tsx
@@ -1,7 +1,7 @@
import { notFound } from 'next/navigation'
export default function page() {
- return 'not found'
+ return 'not-found-text'
}
export async function generateMetadata() {
diff --git a/test/e2e/app-dir/metadata/app/async/redirect/page.tsx b/test/e2e/app-dir/metadata/app/async/redirect/page.tsx
new file mode 100644
index 0000000000000..8f32a92507fa0
--- /dev/null
+++ b/test/e2e/app-dir/metadata/app/async/redirect/page.tsx
@@ -0,0 +1,9 @@
+import { redirect } from 'next/navigation'
+
+export default function page() {
+ return 'redirect to basic'
+}
+
+export async function generateMetadata() {
+ redirect('/basic')
+}
diff --git a/test/e2e/app-dir/metadata/metadata.test.ts b/test/e2e/app-dir/metadata/metadata.test.ts
index a168ce32828b4..b01cad5b7dc79 100644
--- a/test/e2e/app-dir/metadata/metadata.test.ts
+++ b/test/e2e/app-dir/metadata/metadata.test.ts
@@ -330,6 +330,17 @@ createNextDescribe(
)
})
+ it('should support notFound and redirect in generateMetadata', async () => {
+ const resNotFound = await next.fetch('/async/not-found')
+ expect(resNotFound.status).toBe(404)
+ const notFoundHtml = await resNotFound.text()
+ expect(notFoundHtml).not.toBe('not-found-text')
+ expect(notFoundHtml).toContain('This page could not be found.')
+
+ const resRedirect = await next.fetch('/async/redirect')
+ expect(resRedirect.status).toBe(307)
+ })
+
if (isNextDev) {
it('should freeze parent resolved metadata to avoid mutating in generateMetadata', async () => {
const pagePath = 'app/mutate/page.tsx'
diff --git a/test/e2e/app-dir/navigation/app/layout.js b/test/e2e/app-dir/navigation/app/layout.js
index e39561ce4ae6b..661e1f90b9ae2 100644
--- a/test/e2e/app-dir/navigation/app/layout.js
+++ b/test/e2e/app-dir/navigation/app/layout.js
@@ -1,4 +1,4 @@
-export const revalidate = 0
+export const dynamic = 'force-dynamic'
export default function Layout({ children }) {
return (
diff --git a/test/integration/image-optimizer/test/content-disposition-type.test.ts b/test/integration/image-optimizer/test/content-disposition-type.test.ts
new file mode 100644
index 0000000000000..606958b11a1ce
--- /dev/null
+++ b/test/integration/image-optimizer/test/content-disposition-type.test.ts
@@ -0,0 +1,13 @@
+import { join } from 'path'
+import { setupTests } from './util'
+
+const appDir = join(__dirname, '../app')
+const imagesDir = join(appDir, '.next', 'cache', 'images')
+
+describe('with contentDispositionType attachment', () => {
+ setupTests({
+ nextConfigImages: { contentDispositionType: 'attachment' },
+ appDir,
+ imagesDir,
+ })
+})
diff --git a/test/integration/image-optimizer/test/util.ts b/test/integration/image-optimizer/test/util.ts
index 065ea5410354f..4f75ab159c7fa 100644
--- a/test/integration/image-optimizer/test/util.ts
+++ b/test/integration/image-optimizer/test/util.ts
@@ -131,7 +131,8 @@ async function fetchWithDuration(
}
export function runTests(ctx) {
- const { isDev, minimumCacheTTL = 60 } = ctx
+ const { isDev, minimumCacheTTL = 60, nextConfigImages } = ctx
+ const { contentDispositionType = 'inline' } = nextConfigImages || {}
let slowImageServer: Awaited>
beforeAll(async () => {
slowImageServer = await serveSlowImage()
@@ -178,7 +179,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="animated.gif"`
+ `${contentDispositionType}; filename="animated.gif"`
)
await expectWidth(res, 50, { expectAnimated: true })
})
@@ -194,7 +195,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="animated.png"`
+ `${contentDispositionType}; filename="animated.png"`
)
await expectWidth(res, 100, { expectAnimated: true })
})
@@ -210,7 +211,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="animated2.png"`
+ `${contentDispositionType}; filename="animated2.png"`
)
await expectWidth(res, 1105, { expectAnimated: true })
})
@@ -226,7 +227,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="animated.webp"`
+ `${contentDispositionType}; filename="animated.webp"`
)
await expectWidth(res, 400, { expectAnimated: true })
})
@@ -247,7 +248,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toMatch(/^Accept(,|$)/)
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.svg"`
+ `${contentDispositionType}; filename="test.svg"`
)
const actual = await res.text()
const expected = await fs.readFile(
@@ -308,7 +309,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toMatch(/^Accept(,|$)/)
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.ico"`
+ `${contentDispositionType}; filename="test.ico"`
)
const actual = await res.text()
const expected = await fs.readFile(
@@ -332,7 +333,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.jpeg"`
+ `${contentDispositionType}; filename="test.jpeg"`
)
})
@@ -350,7 +351,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.png"`
+ `${contentDispositionType}; filename="test.png"`
)
})
@@ -368,7 +369,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.jpeg"`
+ `${contentDispositionType}; filename="test.jpeg"`
)
await expectWidth(res, ctx.w)
})
@@ -388,7 +389,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.jpeg"`
+ `${contentDispositionType}; filename="test.jpeg"`
)
await expectWidth(res, ctx.w)
})
@@ -532,7 +533,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.webp"`
+ `${contentDispositionType}; filename="test.webp"`
)
await expectWidth(res, ctx.w)
})
@@ -549,7 +550,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.png"`
+ `${contentDispositionType}; filename="test.png"`
)
await expectWidth(res, ctx.w)
})
@@ -566,7 +567,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.png"`
+ `${contentDispositionType}; filename="test.png"`
)
await expectWidth(res, ctx.w)
})
@@ -583,7 +584,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.gif"`
+ `${contentDispositionType}; filename="test.gif"`
)
// FIXME: await expectWidth(res, ctx.w)
})
@@ -600,7 +601,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.tiff"`
+ `${contentDispositionType}; filename="test.tiff"`
)
// FIXME: await expectWidth(res, ctx.w)
})
@@ -619,7 +620,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.webp"`
+ `${contentDispositionType}; filename="test.webp"`
)
await expectWidth(res, ctx.w)
})
@@ -641,7 +642,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.avif"`
+ `${contentDispositionType}; filename="test.avif"`
)
// TODO: upgrade "image-size" package to support AVIF
// See https://github.com/image-size/image-size/issues/348
@@ -675,7 +676,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.webp"`
+ `${contentDispositionType}; filename="test.webp"`
)
await expectWidth(res, ctx.w)
})
@@ -698,7 +699,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="png-as-octet-stream.webp"`
+ `${contentDispositionType}; filename="png-as-octet-stream.webp"`
)
await expectWidth(res, ctx.w)
})
@@ -722,7 +723,7 @@ export function runTests(ctx) {
expect(one.res.headers.get('X-Nextjs-Cache')).toBe('MISS')
expect(one.res.headers.get('Content-Type')).toBe('image/webp')
expect(one.res.headers.get('Content-Disposition')).toBe(
- `inline; filename="slow.webp"`
+ `${contentDispositionType}; filename="slow.webp"`
)
const etagOne = one.res.headers.get('etag')
@@ -746,7 +747,7 @@ export function runTests(ctx) {
expect(two.res.headers.get('X-Nextjs-Cache')).toBe('HIT')
expect(two.res.headers.get('Content-Type')).toBe('image/webp')
expect(two.res.headers.get('Content-Disposition')).toBe(
- `inline; filename="slow.webp"`
+ `${contentDispositionType}; filename="slow.webp"`
)
const json2 = await fsToJson(ctx.imagesDir)
expect(json2).toStrictEqual(json1)
@@ -765,7 +766,7 @@ export function runTests(ctx) {
expect(three.res.headers.get('X-Nextjs-Cache')).toBe('STALE')
expect(three.res.headers.get('Content-Type')).toBe('image/webp')
expect(three.res.headers.get('Content-Disposition')).toBe(
- `inline; filename="slow.webp"`
+ `${contentDispositionType}; filename="slow.webp"`
)
expect(four.duration).toBeLessThan(one.duration)
@@ -773,7 +774,7 @@ export function runTests(ctx) {
expect(four.res.headers.get('X-Nextjs-Cache')).toBe('STALE')
expect(four.res.headers.get('Content-Type')).toBe('image/webp')
expect(four.res.headers.get('Content-Disposition')).toBe(
- `inline; filename="slow.webp"`
+ `${contentDispositionType}; filename="slow.webp"`
)
await check(async () => {
const json4 = await fsToJson(ctx.imagesDir)
@@ -796,7 +797,7 @@ export function runTests(ctx) {
expect(five.res.headers.get('X-Nextjs-Cache')).toBe('HIT')
expect(five.res.headers.get('Content-Type')).toBe('image/webp')
expect(five.res.headers.get('Content-Disposition')).toBe(
- `inline; filename="slow.webp"`
+ `${contentDispositionType}; filename="slow.webp"`
)
await check(async () => {
const json5 = await fsToJson(ctx.imagesDir)
@@ -868,7 +869,7 @@ export function runTests(ctx) {
expect(one.res.headers.get('X-Nextjs-Cache')).toBe('MISS')
expect(one.res.headers.get('Content-Type')).toBe('image/webp')
expect(one.res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.webp"`
+ `${contentDispositionType}; filename="test.webp"`
)
const etagOne = one.res.headers.get('etag')
@@ -892,7 +893,7 @@ export function runTests(ctx) {
expect(two.res.headers.get('X-Nextjs-Cache')).toBe('HIT')
expect(two.res.headers.get('Content-Type')).toBe('image/webp')
expect(two.res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.webp"`
+ `${contentDispositionType}; filename="test.webp"`
)
const json2 = await fsToJson(ctx.imagesDir)
expect(json2).toStrictEqual(json1)
@@ -911,7 +912,7 @@ export function runTests(ctx) {
expect(three.res.headers.get('X-Nextjs-Cache')).toBe('STALE')
expect(three.res.headers.get('Content-Type')).toBe('image/webp')
expect(three.res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.webp"`
+ `${contentDispositionType}; filename="test.webp"`
)
expect(four.duration).toBeLessThan(one.duration)
@@ -919,7 +920,7 @@ export function runTests(ctx) {
expect(four.res.headers.get('X-Nextjs-Cache')).toBe('STALE')
expect(four.res.headers.get('Content-Type')).toBe('image/webp')
expect(four.res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.webp"`
+ `${contentDispositionType}; filename="test.webp"`
)
await check(async () => {
const json3 = await fsToJson(ctx.imagesDir)
@@ -942,7 +943,7 @@ export function runTests(ctx) {
expect(five.res.headers.get('X-Nextjs-Cache')).toBe('HIT')
expect(five.res.headers.get('Content-Type')).toBe('image/webp')
expect(five.res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.webp"`
+ `${contentDispositionType}; filename="test.webp"`
)
await check(async () => {
const json5 = await fsToJson(ctx.imagesDir)
@@ -968,7 +969,7 @@ export function runTests(ctx) {
expect(res1.headers.get('X-Nextjs-Cache')).toBe('MISS')
expect(res1.headers.get('Content-Type')).toBe('image/svg+xml')
expect(res1.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.svg"`
+ `${contentDispositionType}; filename="test.svg"`
)
const etagOne = res1.headers.get('etag')
@@ -987,7 +988,7 @@ export function runTests(ctx) {
expect(res2.headers.get('X-Nextjs-Cache')).toBe('HIT')
expect(res2.headers.get('Content-Type')).toBe('image/svg+xml')
expect(res2.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.svg"`
+ `${contentDispositionType}; filename="test.svg"`
)
const json2 = await fsToJson(ctx.imagesDir)
expect(json2).toStrictEqual(json1)
@@ -1005,7 +1006,7 @@ export function runTests(ctx) {
expect(res1.headers.get('X-Nextjs-Cache')).toBe('MISS')
expect(res1.headers.get('Content-Type')).toBe('image/gif')
expect(res1.headers.get('Content-Disposition')).toBe(
- `inline; filename="animated.gif"`
+ `${contentDispositionType}; filename="animated.gif"`
)
let json1
@@ -1019,7 +1020,7 @@ export function runTests(ctx) {
expect(res2.headers.get('X-Nextjs-Cache')).toBe('HIT')
expect(res2.headers.get('Content-Type')).toBe('image/gif')
expect(res2.headers.get('Content-Disposition')).toBe(
- `inline; filename="animated.gif"`
+ `${contentDispositionType}; filename="animated.gif"`
)
const json2 = await fsToJson(ctx.imagesDir)
expect(json2).toStrictEqual(json1)
@@ -1039,7 +1040,7 @@ export function runTests(ctx) {
const etag = res1.headers.get('Etag')
expect(etag).toBeTruthy()
expect(res1.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.webp"`
+ `${contentDispositionType}; filename="test.webp"`
)
await expectWidth(res1, ctx.w)
@@ -1066,7 +1067,7 @@ export function runTests(ctx) {
expect(res3.headers.get('Etag')).toBeTruthy()
expect(res3.headers.get('Etag')).not.toBe(etag)
expect(res3.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.webp"`
+ `${contentDispositionType}; filename="test.webp"`
)
await expectWidth(res3, ctx.w)
})
@@ -1088,7 +1089,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toMatch(/^Accept(,|$)/)
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.bmp"`
+ `${contentDispositionType}; filename="test.bmp"`
)
await check(async () => {
@@ -1113,7 +1114,7 @@ export function runTests(ctx) {
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('etag')).toBeTruthy()
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="test.webp"`
+ `${contentDispositionType}; filename="test.webp"`
)
await expectWidth(res, 400)
})
@@ -1134,7 +1135,7 @@ export function runTests(ctx) {
)
expect(res.headers.get('Vary')).toBe('Accept')
expect(res.headers.get('Content-Disposition')).toBe(
- `inline; filename="grayscale.png"`
+ `${contentDispositionType}; filename="grayscale.png"`
)
const png = await res.buffer()
@@ -1163,7 +1164,7 @@ export function runTests(ctx) {
)
expect(res1.headers.get('Vary')).toBe('Accept')
expect(res1.headers.get('Content-Disposition')).toBe(
- `inline; filename="${filename}.webp"`
+ `${contentDispositionType}; filename="${filename}.webp"`
)
await expectWidth(res1, ctx.w)
@@ -1175,7 +1176,7 @@ export function runTests(ctx) {
)
expect(res2.headers.get('Vary')).toBe('Accept')
expect(res2.headers.get('Content-Disposition')).toBe(
- `inline; filename="${filename}.webp"`
+ `${contentDispositionType}; filename="${filename}.webp"`
)
await expectWidth(res2, ctx.w)
}
@@ -1223,15 +1224,15 @@ export function runTests(ctx) {
expect(res1.headers.get('Content-Type')).toBe('image/webp')
expect(res1.headers.get('Content-Disposition')).toBe(
- `inline; filename="slow.webp"`
+ `${contentDispositionType}; filename="slow.webp"`
)
expect(res2.headers.get('Content-Type')).toBe('image/webp')
expect(res2.headers.get('Content-Disposition')).toBe(
- `inline; filename="slow.webp"`
+ `${contentDispositionType}; filename="slow.webp"`
)
expect(res3.headers.get('Content-Type')).toBe('image/webp')
expect(res3.headers.get('Content-Disposition')).toBe(
- `inline; filename="slow.webp"`
+ `${contentDispositionType}; filename="slow.webp"`
)
await expectWidth(res1, ctx.w)
@@ -1290,6 +1291,10 @@ export const setupTests = (ctx) => {
// only run one server config with outdated sharp
if (!ctx.isOutdatedSharp) {
describe('dev support w/o next.config.js', () => {
+ if (ctx.nextConfigImages) {
+ // skip this test because it requires next.config.js
+ return
+ }
const size = 384 // defaults defined in server/config.ts
const curCtx = {
...ctx,
@@ -1337,6 +1342,7 @@ export const setupTests = (ctx) => {
imageSizes: [size],
domains: curCtx.domains,
formats: ['image/avif', 'image/webp'],
+ ...ctx.nextConfigImages,
},
})
curCtx.nextOutput = ''
@@ -1364,6 +1370,10 @@ export const setupTests = (ctx) => {
})
describe('Server support w/o next.config.js', () => {
+ if (ctx.nextConfigImages) {
+ // skip this test because it requires next.config.js
+ return
+ }
const size = 384 // defaults defined in server/config.ts
const curCtx = {
...ctx,
@@ -1410,6 +1420,7 @@ export const setupTests = (ctx) => {
formats: ['image/avif', 'image/webp'],
deviceSizes: [size, largeSize],
domains: ctx.domains,
+ ...ctx.nextConfigImages,
},
})
curCtx.nextOutput = ''