Skip to content

Commit

Permalink
Merge branch 'canary' into uploadcare-loader
Browse files Browse the repository at this point in the history
  • Loading branch information
foxeyes authored Nov 3, 2020
2 parents b743cbf + 8277d4d commit d7a0b99
Show file tree
Hide file tree
Showing 44 changed files with 1,200 additions and 569 deletions.
15 changes: 15 additions & 0 deletions docs/advanced-features/i18n-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,21 @@ When using Domain Routing, if a user with the `Accept-Language` header `fr;q=0.9

When using Sub-path Routing, the user would be redirected to `/fr`.

### Disabling Automatic Locale Detection

The automatic locale detection can be disabled with:

```js
// next.config.js
module.exports = {
i18n: {
localeDetection: false,
},
}
```

When `localeDetection` is set to `false` Next.js will no longer automatically redirect based on the user's preferred locale and will only provide locale information detected from either the locale based domain or locale path as described above.

## Accessing the locale information

You can access the locale information via the Next.js router. For example, using the [`useRouter()`](https://nextjs.org/docs/api-reference/next/router#userouter) hook the following properties are available:
Expand Down
4 changes: 2 additions & 2 deletions docs/api-reference/next/image.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export default Home
`Image` accepts the following props:

- `src` - The path or URL to the source image. This is required.
- `width` - The intrinsic width of the source image in pixels. Must be an integer without a unit. Required unless `layout="fill"`.
- `height` - The intrinsic height of the source image, in pixels. Must be an integer without a unit. Required unless `layout="fill"`.
- `width` - The width of the image, in pixels. Must be an integer without a unit. Required unless `layout="fill"`.
- `height` - The height of the image, in pixels. Must be an integer without a unit. Required unless `layout="fill"`.
- `layout` - The rendered layout of the image. If `fixed`, the image dimensions will not change as the viewport changes (no responsiveness). If `intrinsic`, the image will scale the dimensions down for smaller viewports but maintain the original dimensions for larger viewports. If `responsive`, the image will scale the dimensions down for smaller viewports and scale up for larger viewports. If `fill`, the image will stretch both width and height to the dimensions of the parent element. Default `intrinsic`.
- `sizes` - Defines what proportion of the screen you expect the image to take up. Recommended, as it helps serve the correct sized image to each device. [More info](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes).
- `quality` - The quality of the optimized image, an integer between 1 and 100 where 100 is the best quality. Default 75.
Expand Down
7 changes: 7 additions & 0 deletions docs/basic-features/data-fetching.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ The `context` parameter is an object containing the following keys:
- `props` - A **required** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization)
- `revalidate` - An **optional** amount in seconds after which a page re-generation can occur. More on [Incremental Static Regeneration](#incremental-static-regeneration)
- `notFound` - An optional boolean value to allow the page to return a 404 status and page. More on [Incremental Static Regeneration](#incremental-static-regeneration)
- `redirect` - An optional redirect value to allow redirecting to internal and external resources. It should match the shape of `{ destination: string, permanent: boolean }`. In some rare cases, you might need to assign a custom status code for older HTTP Clients to properly redirect. In these cases, you can use the `statusCode` property instead of the `permanent` property, but not both.

> **Note**: You can import modules in top-level scope for use in `getStaticProps`.
> Imports used in `getStaticProps` will [not be bundled for the client-side](#write-server-side-code-directly).
Expand Down Expand Up @@ -552,6 +553,12 @@ The `context` parameter is an object containing the following keys:
- `locales` contains all supported locales (if enabled).
- `defaultLocale` contains the configured default locale (if enabled).

`getServerSideProps` should return an object with:

- `props` - A **required** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization)
- `notFound` - An optional boolean value to allow the page to return a 404 status and page. More on [Incremental Static Regeneration](#incremental-static-regeneration)
- `redirect` - An optional redirect value to allow redirecting to internal and external resources. It should match the shape of `{ destination: string, permanent: boolean }`. In some rare cases, you might need to assign a custom status code for older HTTP Clients to properly redirect. In these cases, you can use the `statusCode` property instead of the `permanent` property, but not both.

> **Note**: You can import modules in top-level scope for use in `getServerSideProps`.
> Imports used in `getServerSideProps` will not be bundled for the client-side.
>
Expand Down
14 changes: 7 additions & 7 deletions docs/basic-features/image-optimization.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ If no configuration is provided, the following default configuration will be use
```js
module.exports = {
images: {
deviceSizes: [320, 420, 768, 1024, 1200],
imageSizes: [],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
domains: [],
path: '/_next/image',
loader: 'default',
Expand All @@ -77,24 +77,24 @@ This means you only need to configure the properties you wish to change.

### Device Sizes

You can specify a list of device width breakpoints using the `deviceSizes` property. Since images maintain their aspect ratio using the `width` and `height` attributes of the source image, there is no need to specify height in `next.config.js` – only the width. These values will be used by the browser to determine which size image should load.
You can specify a list of device width breakpoints using the `deviceSizes` property. These widths are used when the [`next/image`](/docs/api-reference/next/image.md) component uses `layout="responsive"` or `layout="fill"` so that the correct image is served for the device visiting your website.

```js
module.exports = {
images: {
deviceSizes: [320, 420, 768, 1024, 1200],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
},
}
```

### Image Sizes

You can specify a list of exact image widths using the `imageSizes` property. These widths should be different than the widths defined in `deviceSizes`. The purpose is for images that don't scale with the browser window, such as icons, badges, or profile images. If the `width` property of a [`next/image`](/docs/api-reference/next/image.md) component matches a value in `imageSizes`, the image will be rendered at that exact width.
You can specify a list of image widths using the `imageSizes` property. These widths should be different than the widths defined in `deviceSizes` because the arrays will be concatentated. These widths are used when the [`next/image`](/docs/api-reference/next/image.md) component uses `layout="fixed"` or `layout="intrinsic"`.

```js
module.exports = {
images: {
imageSizes: [16, 32, 64],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
}
```
Expand Down Expand Up @@ -170,7 +170,7 @@ The expiration (or rather Max Age) is defined by the upstream server's `Cache-Co

If `s-maxage` is found in `Cache-Control`, it is used. If no `s-maxage` is found, then `max-age` is used. If no `max-age` is found, then 60 seconds is used.

You can configure [`deviceSizes`](#device-sizes) to reduce the total number of possible generated images.
You can configure [`deviceSizes`](#device-sizes) and [`imageSizes`](#device-sizes) to reduce the total number of possible generated images.

## Related

Expand Down
4 changes: 2 additions & 2 deletions errors/invalid-images-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ Make sure your `images` field follows the allowed config shape and values:
module.exports = {
images: {
// limit of 25 deviceSizes values
deviceSizes: [320, 420, 768, 1024, 1200],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
// limit of 25 imageSizes values
imageSizes: [],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
// limit of 50 domains values
domains: [],
path: '/_next/image',
Expand Down
2 changes: 1 addition & 1 deletion packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ export default async function build(
}
}

if (isSsg && !isFallback) {
if (isSsg) {
// remove non-locale prefixed variant from defaultMap
delete defaultMap[page]
}
Expand Down
15 changes: 10 additions & 5 deletions packages/next/build/webpack/loaders/next-serverless-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,12 @@ const nextServerlessLoader: loader.Loader = function () {
let locales = i18n.locales
let defaultLocale = i18n.defaultLocale
let detectedLocale = detectLocaleCookie(req, i18n.locales)
let acceptPreferredLocale = accept.language(
req.headers['accept-language'],
i18n.locales
)
let acceptPreferredLocale = i18n.localeDetection !== false
? accept.language(
req.headers['accept-language'],
i18n.locales
)
: detectedLocale
const { host } = req.headers || {}
// remove port from host and remove port if present
Expand Down Expand Up @@ -368,7 +370,10 @@ const nextServerlessLoader: loader.Loader = function () {
return
}
detectedLocale = detectedLocale || defaultLocale
detectedLocale =
localePathResult.detectedLocale ||
(detectedDomain && detectedDomain.defaultLocale) ||
defaultLocale
`
: `
const i18n = {}
Expand Down
43 changes: 24 additions & 19 deletions packages/next/client/image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ const {
domains: configDomains,
} = imageData
// sort smallest to largest
const allSizes = [...configDeviceSizes, ...configImageSizes]
configDeviceSizes.sort((a, b) => a - b)
configImageSizes.sort((a, b) => a - b)
allSizes.sort((a, b) => a - b)

let cachedObserver: IntersectionObserver

Expand Down Expand Up @@ -106,28 +107,26 @@ function unLazifyImage(lazyImage: HTMLImageElement): void {
lazyImage.classList.remove('__lazy')
}

function getDeviceSizes(
function getSizes(
width: number | undefined,
layout: LayoutValue
): number[] {
): { sizes: number[]; kind: 'w' | 'x' } {
if (
typeof width !== 'number' ||
layout === 'fill' ||
layout === 'responsive'
) {
return configDeviceSizes
return { sizes: configDeviceSizes, kind: 'w' }
}
if (configImageSizes.includes(width)) {
return [width]
}
const widths: number[] = []
for (let size of configDeviceSizes) {
widths.push(size)
if (size >= width) {
break
}
}
return widths

const sizes = [
...new Set(
[width, width * 2, width * 3].map(
(w) => allSizes.find((p) => p >= w) || allSizes[allSizes.length - 1]
)
),
]
return { sizes, kind: 'x' }
}

function computeSrc(
Expand All @@ -140,8 +139,8 @@ function computeSrc(
if (unoptimized) {
return src
}
const widths = getDeviceSizes(width, layout)
const largest = widths[widths.length - 1]
const { sizes } = getSizes(width, layout)
const largest = sizes[sizes.length - 1]
return callLoader({ src, width: largest, quality })
}

Expand Down Expand Up @@ -177,8 +176,14 @@ function generateSrcSet({
return undefined
}

return getDeviceSizes(width, layout)
.map((w) => `${callLoader({ src, width: w, quality })} ${w}w`)
const { sizes, kind } = getSizes(width, layout)
return sizes
.map(
(size, i) =>
`${callLoader({ src, width: size, quality })} ${
kind === 'w' ? size : i + 1
}${kind}`
)
.join(', ')
}

Expand Down
14 changes: 12 additions & 2 deletions packages/next/client/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,21 @@ if (process.env.__NEXT_I18N_SUPPORT) {
detectDomainLocale,
} = require('../next-server/lib/i18n/detect-domain-locale') as typeof import('../next-server/lib/i18n/detect-domain-locale')

const {
parseRelativeUrl,
} = require('../next-server/lib/router/utils/parse-relative-url') as typeof import('../next-server/lib/router/utils/parse-relative-url')

const {
formatUrl,
} = require('../next-server/lib/router/utils/format-url') as typeof import('../next-server/lib/router/utils/format-url')

if (locales) {
const localePathResult = normalizeLocalePath(asPath, locales)
const parsedAs = parseRelativeUrl(asPath)
const localePathResult = normalizeLocalePath(parsedAs.pathname, locales)

if (localePathResult.detectedLocale) {
asPath = asPath.substr(localePathResult.detectedLocale.length + 1) || '/'
parsedAs.pathname = localePathResult.pathname
asPath = formatUrl(parsedAs)
} else {
// derive the default locale if it wasn't detected in the asPath
// since we don't prerender static pages with all possible default
Expand Down
10 changes: 8 additions & 2 deletions packages/next/client/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ function Link(props: React.PropsWithChildren<LinkProps>) {
}, [pathname, props.href, props.as])

let { children, replace, shallow, scroll, locale } = props

// Deprecated. Warning shown by propType check. If the children provided is a string (<Link>example</Link>) we wrap it in an <a> tag
if (typeof children === 'string') {
children = <a>{children}</a>
Expand All @@ -298,7 +299,12 @@ function Link(props: React.PropsWithChildren<LinkProps>) {
const isPrefetched = prefetched[href + '%' + as]
if (!isPrefetched) {
cleanup.current = listenToIntersections(el, () => {
prefetch(router, href, as)
prefetch(router, href, as, {
locale:
typeof locale !== 'undefined'
? locale
: router && router.locale,
})
})
}
}
Expand All @@ -310,7 +316,7 @@ function Link(props: React.PropsWithChildren<LinkProps>) {
}
}
},
[p, childRef, href, as, router]
[p, childRef, href, as, router, locale]
)

const childProps: {
Expand Down
9 changes: 7 additions & 2 deletions packages/next/client/page-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,12 @@ export default class PageLoader {
* @param {string} href the route href (file-system path)
* @param {string} asPath the URL as shown in browser (virtual path); used for dynamic routes
*/
getDataHref(href: string, asPath: string, ssg: boolean, locale?: string) {
getDataHref(
href: string,
asPath: string,
ssg: boolean,
locale?: string | false
) {
const { pathname: hrefPathname, query, search } = parseRelativeUrl(href)
const { pathname: asPathname } = parseRelativeUrl(asPath)
const route = normalizeRoute(hrefPathname)
Expand All @@ -229,7 +234,7 @@ export default class PageLoader {
* @param {string} href the route href (file-system path)
* @param {string} asPath the URL as shown in browser (virtual path); used for dynamic routes
*/
prefetchData(href: string, asPath: string, locale?: string) {
prefetchData(href: string, asPath: string, locale?: string | false) {
const { pathname: hrefPathname } = parseRelativeUrl(href)
const route = normalizeRoute(hrefPathname)
return this.promisedSsgManifest!.then(
Expand Down
2 changes: 1 addition & 1 deletion packages/next/lib/load-custom-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type Header = {
headers: Array<{ key: string; value: string }>
}

const allowedStatusCodes = new Set([301, 302, 303, 307, 308])
export const allowedStatusCodes = new Set([301, 302, 303, 307, 308])

export function getRedirectStatus(route: Redirect): number {
return (
Expand Down
37 changes: 34 additions & 3 deletions packages/next/next-server/lib/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ export function addLocale(
defaultLocale?: string
) {
if (process.env.__NEXT_I18N_SUPPORT) {
return locale && locale !== defaultLocale && !path.startsWith('/' + locale)
return locale &&
locale !== defaultLocale &&
!path.startsWith('/' + locale + '/') &&
path !== '/' + locale
? addPathPrefix(path, '/' + locale)
: path
}
Expand All @@ -71,7 +74,8 @@ export function addLocale(

export function delLocale(path: string, locale?: string) {
if (process.env.__NEXT_I18N_SUPPORT) {
return locale && path.startsWith('/' + locale)
return locale &&
(path.startsWith('/' + locale + '/') || path === '/' + locale)
? path.substr(locale.length + 1) || '/'
: path
}
Expand Down Expand Up @@ -269,6 +273,7 @@ export type NextRouter = BaseRouter &

export type PrefetchOptions = {
priority?: boolean
locale?: string | false
}

export type PrivateRouteInfo = {
Expand Down Expand Up @@ -853,6 +858,12 @@ export default class Router implements BaseRouter {
window.scrollTo((options as any)._N_X, (options as any)._N_Y)
}
}

if (process.env.__NEXT_I18N_SUPPORT) {
if (this.locale) {
document.documentElement.lang = this.locale
}
}
Router.events.emit('routeChangeComplete', as)

return true
Expand Down Expand Up @@ -1171,6 +1182,26 @@ export default class Router implements BaseRouter {

let { pathname } = parsed

if (process.env.__NEXT_I18N_SUPPORT) {
const normalizeLocalePath = require('../i18n/normalize-locale-path')
.normalizeLocalePath as typeof import('../i18n/normalize-locale-path').normalizeLocalePath

if (options.locale === false) {
pathname = normalizeLocalePath!(pathname, this.locales).pathname
parsed.pathname = pathname
url = formatWithValidation(parsed)

let parsedAs = parseRelativeUrl(asPath)
const localePathResult = normalizeLocalePath!(
parsedAs.pathname,
this.locales
)
parsedAs.pathname = localePathResult.pathname
options.locale = localePathResult.detectedLocale || options.locale
asPath = formatWithValidation(parsedAs)
}
}

const pages = await this.pageLoader.getPageList()

parsed = this._resolveHref(parsed, pages) as typeof parsed
Expand All @@ -1190,7 +1221,7 @@ export default class Router implements BaseRouter {
this.pageLoader.prefetchData(
url,
asPath,
this.locale,
typeof options.locale !== 'undefined' ? options.locale : this.locale,
this.defaultLocale
),
this.pageLoader[options.priority ? 'loadPage' : 'prefetch'](route),
Expand Down
Loading

0 comments on commit d7a0b99

Please sign in to comment.