Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add codemods for next/image #41004

Merged
merged 21 commits into from Oct 11, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 49 additions & 0 deletions docs/advanced-features/codemods.md
Expand Up @@ -17,6 +17,55 @@ Codemods are transformations that run on your codebase programmatically. This al
- `--dry` Do a dry-run, no code will be edited
- `--print` Prints the changed output for comparison

## Next.js 13
balazsorban44 marked this conversation as resolved.
Show resolved Hide resolved

### `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.

For example:

```jsx
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>
)
}
```

Transforms into:

```jsx
import Image1 from 'next/legacy/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>
)
}
```

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

Dangerously migrates from `next/legacy/image` to the new `next/image` by adding inline styles and removing unused props.

- Removes `layout` prop and adds `style`
- Removes `objectFit` prop and adds `style`
- Removes `objectPosition` prop and adds `style`
- Removes `lazyBoundary` prop
- Removes `lazyRoot` prop
- TODO: handle `loader`

## Next.js 11

### `cra-to-next` (experimental)
Expand Down
5 changes: 5 additions & 0 deletions docs/upgrading.md
Expand Up @@ -4,6 +4,11 @@ description: Learn how to upgrade Next.js.

# Upgrade Guide

## Upgrading from 12 to 13

The `next/image` import was renamed to `next/legacy/image`. The `next/future/image` import was renamed to `next/image`.
A [codemod is available](/docs/advanced-features/codemods.md#next-image-to-legacy-image) to safely and automatically rename your imports.

## Upgrading to 12.2

If you were using Middleware prior to `12.2`, please see the [upgrade guide](https://nextjs.org/docs/messages/middleware-upgrade-guide) for more information.
Expand Down
@@ -0,0 +1,33 @@
import Image from "next/legacy/image";
import Named, { Blarg } from "next/legacy/image";
import Foo from "foo";
import img from "../public/img.jpg";

export default function Home() {
const myStyle = { color: 'black' };
return (
<div>
<h1>Upgrade</h1>
<Image src="/test.jpg" width="200" height="300" />
<Image src="/test.jpg" width="200" height="300" layout="intrinsic" />
<Image src="/test.jpg" width="200" height="300" layout="responsive" />
<Image src="/test.jpg" width="200" height="300" layout="fixed" />
<div style={{ position: 'relative', width: '300px', height: '500px' }}>
<Image
alt="example alt text"
src={img}
layout="fill"
objectFit="contain"
/>
</div>
<Named src="/test.png" width="500" height="400" layout="fixed" />
<Foo bar="1" />
<Image src="/test.jpg" width="200" height="300" layout="responsive" style={{color: "green"}} />
<Image src="/test.jpg" width="200" height="300" layout="responsive" style={{color: myStyle.color}} />
<Image src="/test.jpg" width="200" height="300" layout="responsive" sizes="50vw" style={{color: "#fff"}} />
<Image src="/test.jpg" width="200" height="300" layout="fixed" lazyBoundary="1500px" lazyRoot={img} />
<Image src="/test.lit" width="200" height="300" layout="responsive" style={myStyle} />
<Image src="/test.dot" width="200" height="300" layout="responsive" style={{...myStyle}} />
</div>
);
}
@@ -0,0 +1,95 @@
import Image from "next/image";
import Named, { Blarg } from "next/image";
import Foo from "foo";
import img from "../public/img.jpg";

export default function Home() {
const myStyle = { color: 'black' };
return (
<div>
<h1>Upgrade</h1>
<Image src="/test.jpg" width="200" height="300" />
<Image
src="/test.jpg"
width="200"
height="300"
style={{
maxWidth: "100%",
height: "auto"
}} />
<Image
src="/test.jpg"
width="200"
height="300"
sizes="100vw"
style={{
width: "100%",
height: "auto"
}} />
<Image src="/test.jpg" width="200" height="300" />
<div style={{ position: 'relative', width: '300px', height: '500px' }}>
<Image
alt="example alt text"
src={img}
fill
sizes="100vw"
style={{
objectFit: "contain"
}} />
</div>
<Named src="/test.png" width="500" height="400" />
<Foo bar="1" />
<Image
src="/test.jpg"
width="200"
height="300"
sizes="100vw"
style={{
color: "green",
width: "100%",
height: "auto"
}} />
<Image
src="/test.jpg"
width="200"
height="300"
sizes="100vw"
style={{
color: myStyle.color,
width: "100%",
height: "auto"
}} />
<Image
src="/test.jpg"
width="200"
height="300"
sizes="50vw"
style={{
color: "#fff",
width: "100%",
height: "auto"
}} />
<Image src="/test.jpg" width="200" height="300" />
<Image
src="/test.lit"
width="200"
height="300"
sizes="100vw"
style={{
...myStyle,
width: "100%",
height: "auto"
}} />
<Image
src="/test.dot"
width="200"
height="300"
sizes="100vw"
style={{
...myStyle,
width: "100%",
height: "auto"
}} />
</div>
);
}
@@ -0,0 +1,14 @@
export async function Home() {
const Image = await import("next/image");
const Named = await import("next/image");
const Foo = await import("foo");
const Future1 = await import("next/future/image");
const Future2 = await import("next/future/image");
return (<div>
<h1>Both</h1>
<Image src="/test.jpg" width="200" height="300" />
<Named src="/test.png" width="500" height="400" />
<Future1 src="/test.webp" width="60" height="70" />
<Future2 src="/test.avif" width="80" height="90" />
</div>)
}
@@ -0,0 +1,14 @@
export async function Home() {
const Image = await import("next/legacy/image");
const Named = await import("next/legacy/image");
const Foo = await import("foo");
const Future1 = await import("next/image");
const Future2 = await import("next/image");
return (<div>
<h1>Both</h1>
<Image src="/test.jpg" width="200" height="300" />
<Named src="/test.png" width="500" height="400" />
<Future1 src="/test.webp" width="60" height="70" />
<Future2 src="/test.avif" width="80" height="90" />
</div>)
}
@@ -0,0 +1,15 @@
import Image from "next/image";
import Named from "next/image";
import Foo from "foo";
import Future1 from "next/future/image";
import Future2 from "next/future/image";

export default function Home() {
return (<div>
<h1>Both</h1>
<Image src="/test.jpg" width="200" height="300" />
<Named src="/test.png" width="500" height="400" />
<Future1 src="/test.webp" width="60" height="70" />
<Future2 src="/test.avif" width="80" height="90" />
</div>)
}
@@ -0,0 +1,15 @@
import Image from "next/legacy/image";
import Named from "next/legacy/image";
import Foo from "foo";
import Future1 from "next/image";
import Future2 from "next/image";

export default function Home() {
return (<div>
<h1>Both</h1>
<Image src="/test.jpg" width="200" height="300" />
<Named src="/test.png" width="500" height="400" />
<Future1 src="/test.webp" width="60" height="70" />
<Future2 src="/test.avif" width="80" height="90" />
</div>)
}
@@ -0,0 +1,16 @@
/* global jest */
jest.autoMockOff()
const defineTest = require('jscodeshift/dist/testUtils').defineTest
const { readdirSync } = require('fs')
const { join } = require('path')

const fixtureDir = 'next-image-experimental'
const fixtureDirPath = join(__dirname, '..', '__testfixtures__', fixtureDir)
const fixtures = readdirSync(fixtureDirPath)
.filter(file => file.endsWith('.input.js'))
.map(file => file.replace('.input.js', ''))

for (const fixture of fixtures) {
const prefix = `${fixtureDir}/${fixture}`;
defineTest(__dirname, fixtureDir, null, prefix)
}
@@ -0,0 +1,16 @@
/* global jest */
jest.autoMockOff()
const defineTest = require('jscodeshift/dist/testUtils').defineTest
const { readdirSync } = require('fs')
const { join } = require('path')

const fixtureDir = 'next-image-to-legacy-image'
const fixtureDirPath = join(__dirname, '..', '__testfixtures__', fixtureDir)
const fixtures = readdirSync(fixtureDirPath)
.filter(file => file.endsWith('.input.js'))
.map(file => file.replace('.input.js', ''))

for (const fixture of fixtures) {
const prefix = `${fixtureDir}/${fixture}`;
defineTest(__dirname, fixtureDir, null, prefix)
}