Codemods
Codemods — это преобразования, которые программно применяются к вашей кодовой базе. Это позволяет автоматически вносить множество изменений без необходимости ручного редактирования каждого файла.
Next.js предоставляет преобразования Codemod для помощи в обновлении кодовой базы Next.js при изменении или устаревании API.
Использование
В терминале перейдите (cd) в папку вашего проекта и выполните:
npx @next/codemod <transform> <path>Замените <transform> и <path> соответствующими значениями.
transform— название преобразованияpath— файлы или директория для преобразования--dryПробный запуск без внесения изменений в код--printВыводит изменённый код для сравнения
Codemods
15.0
Преобразование значения runtime в конфигурации сегментов маршрута App Router с experimental-edge на edge
app-dir-runtime-config-experimental-edge
Примечание: Этот codemod специфичен для App Router.
npx @next/codemod@latest app-dir-runtime-config-experimental-edge .Этот codemod преобразует значение runtime в конфигурации сегментов маршрута с experimental-edge на edge.
Например:
export const runtime = 'experimental-edge'Преобразуется в:
export const runtime = 'edge'Миграция на асинхронные Dynamic API
API, поддерживающие динамический рендеринг, которые ранее работали синхронно, теперь стали асинхронными. Подробнее об этом критическом изменении можно прочитать в руководстве по обновлению.
next-async-request-api
npx @next/codemod@latest next-async-request-api .Этот codemod преобразует динамические API (cookies(), headers() и draftMode() из next/headers), которые теперь асинхронны, добавляя await или оборачивая их в React.use(), если применимо.
Если автоматическая миграция невозможна, codemod добавит приведение типов (для TypeScript-файлов) или комментарий, информирующий о необходимости ручного обновления.
Например:
import { cookies, headers } from 'next/headers'
const token = cookies().get('token')
function useToken() {
const token = cookies().get('token')
return token
}
export default function Page() {
const name = cookies().get('name')
}
function getHeader() {
return headers().get('x-foo')
}Преобразуется в:
import { use } from 'react'
import {
cookies,
headers,
type UnsafeUnwrappedCookies,
type UnsafeUnwrappedHeaders,
} from 'next/headers'
const token = (cookies() as unknown as UnsafeUnwrappedCookies).get('token')
function useToken() {
const token = use(cookies()).get('token')
return token
}
export default async function Page() {
const name = (await cookies()).get('name')
}
function getHeader() {
return (headers() as unknown as UnsafeUnwrappedHeaders).get('x-foo')
}При обнаружении доступа к свойствам params или searchParams в страницах / маршрутах (page.js, layout.js, route.js или default.js) или в API generateMetadata / generateViewport,
codemod попытается преобразовать вызов из синхронного в асинхронный и добавить await для доступа к свойствам. Если это невозможно (например, в Client Component), будет использован React.use для разворачивания промиса.
Например:
// page.tsx
export default function Page({
params,
searchParams,
}: {
params: { slug: string }
searchParams: { [key: string]: string | string[] | undefined }
}) {
const { value } = searchParams
if (value === 'foo') {
// ...
}
}
export function generateMetadata({ params }: { params: { slug: string } }) {
const { slug } = params
return {
title: `My Page - ${slug}`,
}
}Преобразуется в:
// page.tsx
export default async function Page(props: {
params: Promise<{ slug: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const searchParams = await props.searchParams
const { value } = searchParams
if (value === 'foo') {
// ...
}
}
export async function generateMetadata(props: {
params: Promise<{ slug: string }>
}) {
const params = await props.params
const { slug } = params
return {
title: `My Page - ${slug}`,
}
}Полезно знать: Когда codemod обнаруживает место, требующее ручного вмешательства, но не может определить точное исправление, он добавит комментарий или приведение типов с пометкой о необходимости ручного обновления. Эти комментарии начинаются с @next/codemod, а приведения типов — с
UnsafeUnwrapped. Сборка будет завершаться ошибкой, пока эти комментарии не будут удалены. Подробнее.
Замена свойств geo и ip в NextRequest на @vercel/functions
next-request-geo-ip
npx @next/codemod@latest next-request-geo-ip .Этот codemod устанавливает @vercel/functions и заменяет свойства geo и ip в NextRequest на соответствующие функции из @vercel/functions.
Например:
import type { NextRequest } from 'next/server'
export function GET(req: NextRequest) {
const { geo, ip } = req
}Преобразуется в:
import type { NextRequest } from 'next/server'
import { geolocation, ipAddress } from '@vercel/functions'
export function GET(req: NextRequest) {
const geo = geolocation(req)
const ip = ipAddress(req)
}14.0
Миграция импортов ImageResponse
next-og-import
npx @next/codemod@latest next-og-import .Этот codemod переносит импорты из next/server в next/og для использования динамической генерации OG-изображений.
Например:
import { ImageResponse } from 'next/server'Преобразуется в:
import { ImageResponse } from 'next/og'Использование экспорта viewport
metadata-to-viewport-export
npx @next/codemod@latest metadata-to-viewport-export .Этот codemod переносит определённые метаданные viewport в экспорт viewport.
Например:
export const metadata = {
title: 'My App',
themeColor: 'dark',
viewport: {
width: 1,
},
}Преобразуется в:
export const metadata = {
title: 'My App',
}
export const viewport = {
width: 1,
themeColor: 'dark',
}13.2
Использование встроенного шрифта
built-in-next-font
npx @next/codemod@latest built-in-next-font .Этот codemod удаляет пакет @next/font и преобразует импорты @next/font во встроенный next/font.
Например:
import { Inter } from '@next/font/google'Преобразуется в:
import { Inter } from 'next/font/google'13.0
Переименование импортов Next Image
next-image-to-legacy-image
npx @next/codemod@latest next-image-to-legacy-image .Безопасно переименовывает импорты next/image в приложениях Next.js 10, 11 или 12 в next/legacy/image для Next.js 13. Также переименовывает next/future/image в next/image.
Например:
import Image1 from 'next/image'
import Image2 from 'next/future/image'
export default function Home() {
return (
<div>
<Image1 src="/test.jpg" width="200" height="300" />
<Image2 src="/test.png" width="500" height="400" />
</div>
)
}Преобразуется в:
// 'next/image' становится 'next/legacy/image'
import Image1 from 'next/legacy/image'
// 'next/future/image' становится 'next/image'
import Image2 from 'next/image'
export default function Home() {
return (
<div>
<Image1 src="/test.jpg" width="200" height="300" />
<Image2 src="/test.png" width="500" height="400" />
</div>
)
}Миграция на новый компонент Image
next-image-experimental
npx @next/codemod@latest next-image-experimental .Опасная миграция с next/legacy/image на новый next/image путём добавления встроенных стилей и удаления неиспользуемых пропсов.
- Удаляет проп
layoutи добавляетstyle. - Удаляет проп
objectFitи добавляетstyle. - Удаляет проп
objectPositionи добавляетstyle. - Удаляет проп
lazyBoundary. - Удаляет проп
lazyRoot.
Удаление тегов <a> из компонентов Link
new-link
npx @next/codemod@latest new-link .Удаляет теги <a> внутри компонентов Link или добавляет проп legacyBehavior для ссылок, которые не могут быть автоматически исправлены.
Например:
<Link href="/about">
<a>About</a>
</Link>
// преобразуется в
<Link href="/about">
About
</Link>
<Link href="/about">
<a onClick={() => console.log('clicked')}>About</a>
</Link>
// преобразуется в
<Link href="/about" onClick={() => console.log('clicked')}>
About
</Link>В случаях, когда автоматическое исправление невозможно, добавляется проп legacyBehavior. Это позволяет приложению продолжать работать со старым поведением для конкретной ссылки.
const Component = () => <a>About</a>
<Link href="/about">
<Component />
</Link>
// становится
<Link href="/about" legacyBehavior>
<Component />
</Link>11
Миграция с Create React App
cra-to-next
npx @next/codemod cra-to-nextМигрирует проект с Create React App на Next.js, создавая Pages Router и необходимую конфигурацию для соответствия поведению. Изначально используется рендеринг только на стороне клиента, чтобы избежать проблем с использованием window при SSR, и может быть постепенно переведён на использование специфичных функций Next.js.
Поделитесь отзывами об этом преобразовании в этом обсуждении.
10
Добавление импортов React
add-missing-react-import
npx @next/codemod add-missing-react-importПреобразует файлы, не содержащие импорт React, добавляя его для работы с новым преобразованием JSX в React.
Например:
export default class Home extends React.Component {
render() {
return <div>Hello World</div>
}
}Преобразуется в:
import React from 'react'
export default class Home extends React.Component {
render() {
return <div>Hello World</div>
}
}9
Преобразование анонимных компонентов в именованные
name-default-component
npx @next/codemod name-default-componentВерсии 9 и выше.
Преобразует анонимные компоненты в именованные, чтобы они работали с Fast Refresh.
Например:
export default function () {
return <div>Hello World</div>
}Преобразуется в:
export default function MyComponent() {
return <div>Hello World</div>
}Компонент получает имя в camelCase на основе имени файла, и это также работает со стрелочными функциями.
8
Преобразование HOC AMP в конфигурацию страницы
withamp-to-config
npx @next/codemod withamp-to-configПреобразует HOC withAmp в конфигурацию страницы для Next.js 9.
Например:
// До
import { withAmp } from 'next/amp'
function Home() {
return <h1>My AMP Page</h1>
}
export default withAmp(Home)// После
export default function Home() {
return <h1>My AMP Page</h1>
}
export const config = {
amp: true,
}6
Использование withRouter
url-to-withrouter
npx @next/codemod url-to-withrouterПреобразует устаревшее автоматически внедряемое свойство url на страницах верхнего уровня в использование withRouter и свойства router, которое оно внедряет. Подробнее: https://nextjs.org/docs/messages/url-deprecated
Например:
import React from 'react'
export default class extends React.Component {
render() {
const { pathname } = this.props.url
return <div>Current pathname: {pathname}</div>
}
}import React from 'react'
import { withRouter } from 'next/router'
export default withRouter(
class extends React.Component {
render() {
const { pathname } = this.props.router
return <div>Current pathname: {pathname}</div>
}
}
)Это один из случаев. Все преобразуемые случаи (и тесты) можно найти в директории __testfixtures__.