Skip to content

Commit 9fc235d

Browse files
Merge branch 'canary' into next-dev-image-trailing-slash
2 parents b829ade + ab0b0a8 commit 9fc235d

File tree

7 files changed

+211
-1
lines changed

7 files changed

+211
-1
lines changed

docs/basic-features/image-optimization.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,30 @@ export default Home
4444

4545
- `width` and `height` are required to prevent [Cumulative Layout Shift](https://web.dev/cls/), a [Core Web Vital](https://web.dev/vitals/) that Google is going to [use in their search ranking](https://webmasters.googleblog.com/2020/05/evaluating-page-experience.html)
4646
- `width` and `height` are automatically responsive, unlike the HTML `<img>` element
47+
- `quality` can be configured per image, default 75
4748
- See [`next/image`](/docs/api-reference/next/image.md) for list of available props.
4849

4950
## Configuration
5051

51-
You can configure Image Optimization by using the `images` property in `next.config.js`.
52+
You can optionally configure Image Optimization by using the `images` property in `next.config.js`.
53+
54+
If no configuration is provided, the following default configuration will be used.
55+
56+
```js
57+
module.exports = {
58+
images: {
59+
deviceSizes: [320, 420, 768, 1024, 1200],
60+
iconSizes: [],
61+
domains: [],
62+
path: '/_next/image',
63+
loader: 'default',
64+
},
65+
}
66+
```
67+
68+
If a specific property is omitted, such as `deviceSizes`, that property will use the default above.
69+
70+
This means you only need to configure the properties you wish to change.
5271

5372
### Device Sizes
5473

packages/next/build/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,9 @@ export default async function build(
10481048
(staticPages.size + ssgPages.size + serverPropsPages.size),
10491049
hasStatic404: useStatic404,
10501050
hasReportWebVitals: namedExports?.includes('reportWebVitals') ?? false,
1051+
rewritesCount: rewrites.length,
1052+
headersCount: headers.length,
1053+
redirectsCount: redirects.length - 1, // reduce one for trailing slash
10511054
})
10521055
)
10531056

packages/next/telemetry/events/build.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ type EventBuildOptimized = {
4545
hasTestPages: boolean
4646
hasStatic404: boolean
4747
hasReportWebVitals: boolean
48+
headersCount: number
49+
rewritesCount: number
50+
redirectsCount: number
4851
}
4952

5053
export function eventBuildOptimize(

packages/next/telemetry/events/version.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ type EventCliSessionStarted = {
2121
buildTarget: string
2222
hasWebpackConfig: boolean
2323
hasBabelConfig: boolean
24+
basePathEnabled: boolean
25+
i18nEnabled: boolean
26+
imageEnabled: boolean
27+
locales: string | null
28+
localeDomainsCount: number | null
29+
localeDetectionEnabled: boolean | null
30+
imageDomainsCount: number | null
31+
imageSizes: string | null
32+
imageLoader: string | null
33+
trailingSlashEnabled: boolean
34+
reactStrictMode: boolean
2435
}
2536

2637
function hasBabelConfig(dir: string): boolean {
@@ -83,6 +94,17 @@ export function eventCliSession(
8394
| 'buildTarget'
8495
| 'hasWebpackConfig'
8596
| 'hasBabelConfig'
97+
| 'basePathEnabled'
98+
| 'i18nEnabled'
99+
| 'imageEnabled'
100+
| 'locales'
101+
| 'localeDomainsCount'
102+
| 'localeDetectionEnabled'
103+
| 'imageDomainsCount'
104+
| 'imageSizes'
105+
| 'imageLoader'
106+
| 'trailingSlashEnabled'
107+
| 'reactStrictMode'
86108
>
87109
): { eventName: string; payload: EventCliSessionStarted }[] {
88110
// This should be an invariant, if it fails our build tooling is broken.
@@ -92,6 +114,9 @@ export function eventCliSession(
92114

93115
const userConfiguration = getNextConfig(phase, dir)
94116

117+
const { images, experimental } = userConfiguration || {}
118+
const { i18n } = experimental || {}
119+
95120
const payload: EventCliSessionStarted = {
96121
nextVersion: process.env.__NEXT_VERSION,
97122
nodeVersion: process.version,
@@ -103,6 +128,17 @@ export function eventCliSession(
103128
buildTarget: userConfiguration?.target ?? 'default',
104129
hasWebpackConfig: typeof userConfiguration?.webpack === 'function',
105130
hasBabelConfig: hasBabelConfig(dir),
131+
imageEnabled: !!images,
132+
basePathEnabled: !!userConfiguration?.basePath,
133+
i18nEnabled: !!i18n,
134+
locales: i18n?.locales ? i18n.locales.join(',') : null,
135+
localeDomainsCount: i18n?.domains ? i18n.domains.length : null,
136+
localeDetectionEnabled: !i18n ? null : i18n.localeDetection !== false,
137+
imageDomainsCount: images?.domains ? images.domains.length : null,
138+
imageSizes: images?.sizes ? images.sizes.join(',') : null,
139+
imageLoader: images?.loader,
140+
trailingSlashEnabled: !!userConfiguration?.trailingSlash,
141+
reactStrictMode: !!userConfiguration?.reactStrictMode,
106142
}
107143
return [{ eventName: EVENT_VERSION, payload }]
108144
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module.exports = phase => {
2+
return {
3+
rewrites() {
4+
return [
5+
{
6+
source: '/hello-1',
7+
destination: '/world'
8+
},
9+
{
10+
source: '/hello-2',
11+
destination: '/world'
12+
},
13+
]
14+
},
15+
redirects() {
16+
return [
17+
{
18+
source: '/hello-3',
19+
destination: '/world',
20+
permanent: false
21+
}
22+
]
23+
},
24+
headers() {
25+
return [
26+
{
27+
source: '/hello-4',
28+
headers: [
29+
{
30+
key: 'x-path',
31+
value: 'hello-4'
32+
}
33+
]
34+
}
35+
]
36+
}
37+
}
38+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module.exports = phase => {
2+
return {
3+
images: {
4+
sizes: [64, 128, 256, 512, 1024],
5+
domains: ['example.com'],
6+
},
7+
experimental: {
8+
i18n: {
9+
locales: ['en','nl','fr'],
10+
defaultLocale: 'en',
11+
domains: [
12+
{
13+
domain: 'example.com',
14+
defaultLocale: 'en'
15+
},
16+
{
17+
domain: 'example.fr',
18+
defaultLocale: 'fr'
19+
}
20+
]
21+
}
22+
}
23+
}
24+
}

test/integration/telemetry/test/index.test.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,4 +420,91 @@ describe('Telemetry CLI', () => {
420420
event1 = /NEXT_BUILD_OPTIMIZED[\s\S]+?{([\s\S]+?)}/.exec(build.stderr).pop()
421421
expect(event1).toMatch(/hasReportWebVitals.*?false/)
422422
})
423+
424+
it('detects rewrites, headers, and redirects for next build', async () => {
425+
await fs.rename(
426+
path.join(appDir, 'next.config.custom-routes'),
427+
path.join(appDir, 'next.config.js')
428+
)
429+
430+
const { stderr } = await nextBuild(appDir, [], {
431+
stderr: true,
432+
env: { NEXT_TELEMETRY_DEBUG: 1 },
433+
})
434+
435+
await fs.rename(
436+
path.join(appDir, 'next.config.js'),
437+
path.join(appDir, 'next.config.custom-routes')
438+
)
439+
440+
const event1 = /NEXT_BUILD_OPTIMIZED[\s\S]+?{([\s\S]+?)}/.exec(stderr).pop()
441+
expect(event1).toMatch(/"headersCount": 1/)
442+
expect(event1).toMatch(/"rewritesCount": 2/)
443+
expect(event1).toMatch(/"redirectsCount": 1/)
444+
})
445+
446+
it('detects i18n and image configs for session start', async () => {
447+
await fs.rename(
448+
path.join(appDir, 'next.config.i18n-images'),
449+
path.join(appDir, 'next.config.js')
450+
)
451+
452+
const { stderr } = await nextBuild(appDir, [], {
453+
stderr: true,
454+
env: { NEXT_TELEMETRY_DEBUG: 1 },
455+
})
456+
457+
await fs.rename(
458+
path.join(appDir, 'next.config.js'),
459+
path.join(appDir, 'next.config.i18n-images')
460+
)
461+
462+
const event1 = /NEXT_CLI_SESSION_STARTED[\s\S]+?{([\s\S]+?)}/
463+
.exec(stderr)
464+
.pop()
465+
466+
expect(event1).toMatch(/"i18nEnabled": true/)
467+
expect(event1).toMatch(/"locales": "en,nl,fr"/)
468+
expect(event1).toMatch(/"localeDomainsCount": 2/)
469+
expect(event1).toMatch(/"localeDetectionEnabled": true/)
470+
expect(event1).toMatch(/"imageDomainsCount": 1/)
471+
expect(event1).toMatch(/"imageSizes": "64,128,256,512,1024"/)
472+
expect(event1).toMatch(/"trailingSlashEnabled": false/)
473+
expect(event1).toMatch(/"reactStrictMode": false/)
474+
475+
await fs.rename(
476+
path.join(appDir, 'next.config.i18n-images'),
477+
path.join(appDir, 'next.config.js')
478+
)
479+
480+
let stderr2 = ''
481+
482+
let app = await launchApp(appDir, await findPort(), {
483+
onStderr(msg) {
484+
stderr2 += msg || ''
485+
},
486+
env: {
487+
NEXT_TELEMETRY_DEBUG: 1,
488+
},
489+
})
490+
await waitFor(1000)
491+
await killApp(app)
492+
493+
await fs.rename(
494+
path.join(appDir, 'next.config.js'),
495+
path.join(appDir, 'next.config.i18n-images')
496+
)
497+
498+
const event2 = /NEXT_CLI_SESSION_STARTED[\s\S]+?{([\s\S]+?)}/
499+
.exec(stderr2)
500+
.pop()
501+
expect(event2).toMatch(/"i18nEnabled": true/)
502+
expect(event2).toMatch(/"locales": "en,nl,fr"/)
503+
expect(event2).toMatch(/"localeDomainsCount": 2/)
504+
expect(event2).toMatch(/"localeDetectionEnabled": true/)
505+
expect(event2).toMatch(/"imageDomainsCount": 1/)
506+
expect(event2).toMatch(/"imageSizes": "64,128,256,512,1024"/)
507+
expect(event2).toMatch(/"trailingSlashEnabled": false/)
508+
expect(event2).toMatch(/"reactStrictMode": false/)
509+
})
423510
})

0 commit comments

Comments
 (0)