Skip to content

Commit

Permalink
Merge branch 'canary' into fix/clarify-conflict-error
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk committed Nov 3, 2022
2 parents 133f36a + 8fa78a5 commit 92aa1c3
Show file tree
Hide file tree
Showing 22 changed files with 567 additions and 157 deletions.
4 changes: 2 additions & 2 deletions docs/advanced-features/codemods.md
Expand Up @@ -45,7 +45,7 @@ export default function Page() {

### `next-image-to-legacy-image`

Safely migrates existing Next.js 10, 11, 12 applications importing `next/image` to the renamed `next/legacy/image` import in Next.js 13.
This codemod safely migrates existing Next.js 10, 11, 12 applications importing `next/image` to the renamed `next/legacy/image` import in Next.js 13.

For example:

Expand Down Expand Up @@ -81,7 +81,7 @@ export default function Home() {

### `next-image-experimental` (experimental)

Dangerously migrates from `next/legacy/image` to the new `next/image` by adding inline styles and removing unused props.
This codemod dangerously migrates from `next/legacy/image` to the new `next/image` by adding inline styles and removing unused props. Please note this codemod is experimental and only covers static usage (such as `<Image src={img} layout="responsive" />`) but not dynamic usage (such as `<Image {...props} />`).

- Removes `layout` prop and adds `style`
- Removes `objectFit` prop and adds `style`
Expand Down
2 changes: 1 addition & 1 deletion docs/api-reference/next/font.md
Expand Up @@ -91,7 +91,7 @@ A string value to define the CSS variable name to be used if the style is applie

### Font function arguments

For usage, review [Local Fonts](/docs/optimizing/fonts#local-fonts).
For usage, review [Local Fonts](/docs/basic-features/font-optimization.md#local-fonts).

| Key | Example | Data type | Required |
| ------------------------------------------- | ----------------------------------------------------------- | -------------------------------------- | -------- |
Expand Down
29 changes: 17 additions & 12 deletions docs/basic-features/font-optimization.md
Expand Up @@ -28,8 +28,9 @@ Import the font you would like to use from `@next/font/google` as a function. We

To use the font in all your pages, add it to [`_app.js` file](https://nextjs.org/docs/advanced-features/custom-app) under `/pages` as shown below:

```js:pages/_app.js
import { Inter } from '@next/font/google';
```js
// pages/_app.js
import { Inter } from '@next/font/google'

// If loading a variable font, you don't need to specify the font weight
const inter = Inter()
Expand All @@ -45,8 +46,9 @@ export default function MyApp({ Component, pageProps }) {

If you can't use a variable font, you will **need to specify a weight**:

```js:pages/_app.js
import { Roboto } from '@next/font/google';
```js
// pages/_app.js
import { Roboto } from '@next/font/google'

const roboto = Roboto({
weight: '400',
Expand All @@ -65,10 +67,11 @@ export default function MyApp({ Component, pageProps }) {

You can also use the font without a wrapper and `className` by injecting it inside the `<head>` as follows:

```js:pages/_app.js
import { Inter } from '@next/font/google';
```js
// pages/_app.js
import { Inter } from '@next/font/google'

const inter = Inter();
const inter = Inter()

export default function MyApp({ Component, pageProps }) {
return (
Expand Down Expand Up @@ -110,8 +113,9 @@ This can be done in 2 ways:

- On a font per font basis by adding it to the function call

```js:pages/_app.js
const inter = Inter({ subsets: ["latin"] });
```js
// pages/_app.js
const inter = Inter({ subsets: ['latin'] })
```

- Globally for all your fonts in your `next.config.js`
Expand All @@ -135,11 +139,12 @@ View the [Font API Reference](/docs/api-reference/next/font.md#nextfontgoogle) f

Import `@next/font/local` and specify the `src` of your local font file. We recommend using [**variable fonts**](https://fonts.google.com/variablefonts) for the best performance and flexibility.

```js:pages/_app.js
import localFont from '@next/font/local';
```js
// pages/_app.js
import localFont from '@next/font/local'

// Font files can be colocated inside of `pages`
const myFont = localFont({ src: './my-font.woff2' });
const myFont = localFont({ src: './my-font.woff2' })

export default function MyApp({ Component, pageProps }) {
return (
Expand Down
2 changes: 1 addition & 1 deletion docs/routing/introduction.md
Expand Up @@ -68,7 +68,7 @@ The example above uses multiple links. Each one maps a path (`href`) to a known
- `/about``pages/about.js`
- `/blog/hello-world``pages/blog/[slug].js`

Any `<Link />` in the viewport (initially or through scroll) will be prefetched by default (including the corresponding data) for pages using [Static Generation](/docs/basic-features/data-fetching/get-static-props.md). The corresponding data for [server-rendered](/docs/basic-features/data-fetching/get-server-side-props.md) routes is fetched _only when_ the <Link /> is clicked.
Any `<Link />` in the viewport (initially or through scroll) will be prefetched by default (including the corresponding data) for pages using [Static Generation](/docs/basic-features/data-fetching/get-static-props.md). The corresponding data for [server-rendered](/docs/basic-features/data-fetching/get-server-side-props.md) routes is fetched _only when_ the `<Link />` is clicked.

### Linking to dynamic paths

Expand Down
2 changes: 1 addition & 1 deletion docs/testing.md
Expand Up @@ -293,7 +293,7 @@ module.exports = createJestConfig(customJestConfig)
Under the hood, `next/jest` is automatically configuring Jest for you, including:

- Setting up `transform` using [SWC](https://nextjs.org/docs/advanced-features/compiler)
- Auto mocking stylesheets (`.css`, `.module.css`, and their scss variants) and image imports
- Auto mocking stylesheets (`.css`, `.module.css`, and their scss variants), image imports and [`@next/font`](https://nextjs.org/docs/basic-features/font-optimization)
- Loading `.env` (and all variants) into `process.env`
- Ignoring `node_modules` from test resolving and transforms
- Ignoring `.next` from test resolving
Expand Down
10 changes: 5 additions & 5 deletions docs/upgrading.md
Expand Up @@ -34,16 +34,16 @@ You can continue using `pages` with new features that work in both directories,

### `<Image/>` Component

Next.js 12 introduced new improvements to the Image Component with a temporary import: `next/future/image`. These improvements included less client-side JavaScript, easier ways to extend and style images, better accessibility, and native browser lazy loading.
Next.js 12 introduced many improvements to the Image Component with a temporary import: `next/future/image`. These improvements included less client-side JavaScript, easier ways to extend and style images, better accessibility, and native browser lazy loading.

In version 13, this new behavior is now the default for `next/image`.
Starting in Next.js 13, this new behavior is now the default for `next/image`.

There are two codemods to help you migrate to the new Image Component:

- [**`next-image-to-legacy-image` codemod**](/docs/advanced-features/codemods.md#rename-instances-of-nextimage): Safely and automatically renames `next/image` imports to `next/legacy/image`. Existing components will maintain the same behavior.
- [**`next-image-experimental` codemod**](/docs/advanced-features/codemods.md#migrate-next-image-experimental-experimental): Dangerously adds inline styles and removes unused props using the experimental. This will change the behavior of existing components to match the new defaults. To use this codemod, you need to run the `next-image-to-legacy-image` codemod first.
- [next-image-to-legacy-image](/docs/advanced-features/codemods.md#rename-instances-of-nextimage): This codemod will safely and automatically rename `next/image` imports to `next/legacy/image` to maintain the same behavior as Next.js 12. We recommend running this codemod to quickly update to Next.js 13 automatically.
- [next-image-experimental](/docs/advanced-features/codemods.md#next-image-experimental-experimental): After running the previous codemod, you can optionally run this experimental codemod to upgrade `next/legacy/image` to the new `next/image`, which will remove unused props and add inline styles. Please note this codemod is experimental and only covers static usage (such as `<Image src={img} layout="responsive" />`) but not dynamic usage (such as `<Image {...props} />`).

Alternatively, you can manually update props by following the [`next/future/image` migration guide](/docs/api-reference/next/image.md#migration). This will change the behavior of existing components to match the new defaults.
Alternatively, you can manually update by following the [migration guide](/docs/advanced-features/codemods.md#next-image-experimental-experimental) and also see the [legacy comparison](/docs/api-reference/next/legacy/image.md#comparison).

### `<Link>` Component

Expand Down
2 changes: 1 addition & 1 deletion errors/next-image-upgrade-to-13.md
Expand Up @@ -31,7 +31,7 @@ After running this codemod, you can optionally upgrade `next/legacy/image` to th
npx @next/codemod next-image-experimental .
```

Please note this second codemod is experimental and only covers static usage, not dynamic usage (such `<Image {...props} />`).
Please note this second codemod is experimental and only covers static usage (such as `<Image src={img} layout="responsive" />`) but not dynamic usage (such as `<Image {...props} />`).

### Useful Links

Expand Down
Expand Up @@ -4,7 +4,7 @@ import { SkeletonCard } from '@/ui/SkeletonCard';

export default function Page({ params }: PageProps) {
const category = use(
fetchSubCategory(params.categorySlug, params.subCategory),
fetchSubCategory(params.categorySlug, params.subCategorySlug),
);
if (!category) return null;
return (
Expand Down
3 changes: 3 additions & 0 deletions jest.config.js
Expand Up @@ -14,6 +14,9 @@ const customJestConfig = {
globals: {
AbortSignal: global.AbortSignal,
},
moduleNameMapper: {
'@next/font/(.*)': '@next/font/$1',
},
}

// createJestConfig is exported in this way to ensure that next/jest can load the Next.js config which is async
Expand Down
34 changes: 14 additions & 20 deletions packages/font/src/google/loader.ts
Expand Up @@ -49,27 +49,21 @@ const downloadGoogleFonts: FontLoader = async ({
)
}

let fontFaceDeclarations = ''
for (const weight of weights) {
for (const style of styles) {
const fontAxes = getFontAxes(
fontFamily,
weight,
style,
selectedVariableAxes
)
const url = getUrl(fontFamily, fontAxes, display)
const fontAxes = getFontAxes(
fontFamily,
weights,
styles,
selectedVariableAxes
)
const url = getUrl(fontFamily, fontAxes, display)

let cachedCssRequest = cssCache.get(url)
const fontFaceDeclaration =
cachedCssRequest ?? (await fetchCSSFromGoogleFonts(url, fontFamily))
if (!cachedCssRequest) {
cssCache.set(url, fontFaceDeclaration)
} else {
cssCache.delete(url)
}
fontFaceDeclarations += `${fontFaceDeclaration}\n`
}
let cachedCssRequest = cssCache.get(url)
const fontFaceDeclarations =
cachedCssRequest ?? (await fetchCSSFromGoogleFonts(url, fontFamily))
if (!cachedCssRequest) {
cssCache.set(url, fontFaceDeclarations)
} else {
cssCache.delete(url)
}

// Find font files to download
Expand Down
90 changes: 66 additions & 24 deletions packages/font/src/google/utils.ts
Expand Up @@ -126,25 +126,50 @@ export function validateData(functionName: string, data: any): FontOptions {

export function getUrl(
fontFamily: string,
axes: [string, string][],
axes: {
wght: string[]
ital: string[]
variableAxes?: [string, string][]
},
display: string
) {
// Variants are all combinations of weight and style, each variant will result in a separate font file
const variants: Array<[string, string][]> = []
for (const wgth of axes.wght) {
if (axes.ital.length === 0) {
variants.push([['wght', wgth], ...(axes.variableAxes ?? [])])
} else {
for (const ital of axes.ital) {
variants.push([
['ital', ital],
['wght', wgth],
...(axes.variableAxes ?? []),
])
}
}
}

// Google api requires the axes to be sorted, starting with lowercase words
axes.sort(([a], [b]) => {
const aIsLowercase = a.charCodeAt(0) > 96
const bIsLowercase = b.charCodeAt(0) > 96
if (aIsLowercase && !bIsLowercase) return -1
if (bIsLowercase && !aIsLowercase) return 1
if (axes.variableAxes) {
variants.forEach((variant) => {
variant.sort(([a], [b]) => {
const aIsLowercase = a.charCodeAt(0) > 96
const bIsLowercase = b.charCodeAt(0) > 96
if (aIsLowercase && !bIsLowercase) return -1
if (bIsLowercase && !aIsLowercase) return 1

return a > b ? 1 : -1
})
return a > b ? 1 : -1
})
})
}

return `https://fonts.googleapis.com/css2?family=${fontFamily.replace(
/ /g,
'+'
)}:${axes.map(([key]) => key).join(',')}@${axes
.map(([, val]) => val)
.join(',')}&display=${display}`
)}:${variants[0].map(([key]) => key).join(',')}@${variants
.map((variant) => variant.map(([, val]) => val).join(','))
.sort()
.join(';')}&display=${display}`
}

export async function fetchCSSFromGoogleFonts(url: string, fontFamily: string) {
Expand Down Expand Up @@ -192,17 +217,23 @@ export async function fetchFontFile(url: string) {

export function getFontAxes(
fontFamily: string,
weight: string,
style: string,
weights: string[],
styles: string[],
selectedVariableAxes?: string[]
): [string, string][] {
): {
wght: string[]
ital: string[]
variableAxes?: [string, string][]
} {
const allAxes: Array<{ tag: string; min: number; max: number }> = (
fontData as any
)[fontFamily].axes
const italicAxis: [string, string][] =
style === 'italic' ? [['ital', '1']] : []
const hasItalic = styles.includes('italic')
const hasNormal = styles.includes('normal')
const ital = hasItalic ? [...(hasNormal ? ['0'] : []), '1'] : []

if (weight === 'variable') {
// Weights will always contain one element if it's a variable font
if (weights[0] === 'variable') {
if (selectedVariableAxes) {
const defineAbleAxes: string[] = allAxes
.map(({ tag }) => tag)
Expand All @@ -228,14 +259,25 @@ export function getFontAxes(
})
}

const variableAxes: [string, string][] = allAxes
.filter(
({ tag }) => tag === 'wght' || selectedVariableAxes?.includes(tag)
)
.map(({ tag, min, max }) => [tag, `${min}..${max}`])
let weightAxis: string
const variableAxes: [string, string][] = []
for (const { tag, min, max } of allAxes) {
if (tag === 'wght') {
weightAxis = `${min}..${max}`
} else if (selectedVariableAxes?.includes(tag)) {
variableAxes.push([tag, `${min}..${max}`])
}
}

return [...italicAxis, ...variableAxes]
return {
wght: [weightAxis!],
ital,
variableAxes,
}
} else {
return [...italicAxis, ['wght', weight]]
return {
ital,
wght: weights,
}
}
}
12 changes: 12 additions & 0 deletions packages/next/build/jest/__mocks__/nextFontMock.js
@@ -0,0 +1,12 @@
module.exports = new Proxy(
{},
{
get: function getter() {
return () => ({
className: 'className',
variable: 'variable',
style: { fontFamily: 'fontFamily' },
})
},
}
)
3 changes: 3 additions & 0 deletions packages/next/build/jest/jest.ts
Expand Up @@ -115,6 +115,9 @@ export default function nextJest(options: { dir?: string } = {}) {
// Keep .svg to it's own rule to make overriding easy
'^.+\\.(svg)$': require.resolve(`./__mocks__/fileMock.js`),

// Handle @next/font
'@next/font/(.*)': require.resolve('./__mocks__/nextFontMock.js'),

// custom config comes last to ensure the above rules are matched,
// fixes the case where @pages/(.*) -> src/pages/$! doesn't break
// CSS/image mocks
Expand Down
19 changes: 9 additions & 10 deletions packages/next/build/swc/options.js
Expand Up @@ -33,7 +33,6 @@ function getBaseSWCOptions({
jsConfig,
swcCacheDir,
isServerLayer,
relativeFilePathFromRoot,
hasServerComponents,
}) {
const parserConfig = getParserOptions({ filename, jsConfig })
Expand Down Expand Up @@ -131,15 +130,6 @@ function getBaseSWCOptions({
isServer: !!isServerLayer,
}
: false,
fontLoaders:
nextConfig?.experimental?.fontLoaders && relativeFilePathFromRoot
? {
fontLoaders: nextConfig.experimental.fontLoaders.map(
({ loader }) => loader
),
relativeFilePathFromRoot,
}
: null,
}
}

Expand Down Expand Up @@ -255,6 +245,15 @@ export function getLoaderSWCOptions({
hasServerComponents,
})

if (nextConfig?.experimental?.fontLoaders && relativeFilePathFromRoot) {
baseOptions.fontLoaders = {
fontLoaders: nextConfig.experimental.fontLoaders.map(
({ loader }) => loader
),
relativeFilePathFromRoot,
}
}

const isNextDist = nextDistPath.test(filename)

if (isServer) {
Expand Down

0 comments on commit 92aa1c3

Please sign in to comment.