Skip to content

Commit

Permalink
feat(next): codemod for removed geo and ip of NextRequest (#70064)
Browse files Browse the repository at this point in the history
> [!NOTE]
> This codemod requires to install the `@vercel/functions` package.

### Why?

Codemod support for breaking change at
#68379. It removed the `ip`, and
`geo` properties from the `NextRequest` type. The `NextRequest` type was
mostly used in the `middleware` and `route`.



### How?

The codemod replaces the `ip`, and `geo` properties from the
`NextRequest` with corresponding `@vercel/functions` features.

#### Accessing NextRequest Value

When a file uses `NextRequest`, we look if there's an access to the type
with `geo` or `ip`. This targets destructuring and direct access.

```ts
export function GET (req: NextRequest) {
  const { geo, ip: ipAlias, pathname } = req // destructuring
  // direct access
  req.geo
  req.ip
}
```

Declares a variable based on the identifier within the object. If it was
aliased, we use the aliased name.

```ts
import { geolocation, ipAddress } from '@vercel/functions'

export function GET (req: NextRequest) {
  const { pathname } = req // destructuring
  const geo = geolocation(req)
  const ipAlias = ipAddress(req)
  // direct access
  geolocation(req)
  ipAddress(req)
}
```

#### Accessing NextRequest Types

When the file has accessed the `geo | ip` type from `NextRequest`, we
replace with the corresponding types.

```ts
import type { NextRequest } from 'next/server'

export function GET (req: NextRequest) {
  const ip = '...' as NextRequest['ip']
  const geo = { ... } as NextRequest['geo']
}
```

Since the type for `ip` is `string | undefined`, we explicitly replace
it. For `geo`, we replace with the `Geo` type.

```ts
import type { NextRequest } from 'next/server'
import type { Geo } from '@vercel/functions'

export function GET (req: NextRequest) {
  const ip = '...' as string | undefined
  const geo = { ... } as Geo
}
```

### Ref

Behavior FYI:
#70064 (comment),
#70064 (comment)
Closes NDX-291

---------

Co-authored-by: Sebastian Silbermann <[email protected]>
Co-authored-by: Donny/강동윤 <[email protected]>
Co-authored-by: Jiachi Liu <[email protected]>
  • Loading branch information
4 people authored Sep 23, 2024
1 parent 60a8a0b commit 0090faa
Show file tree
Hide file tree
Showing 39 changed files with 945 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@
"RUST_BACKTRACE": "0"
},
"cSpell.words": [
"Destructuring",
"Entrypoints",
"jscodeshift",
"napi",
"nextjs",
"opentelemetry",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,40 @@ Replacing `<transform>` and `<path>` with appropriate values.

## Next.js Codemods

### 15.0

#### Replace `geo` and `ip` properties of `NextRequest` with `@vercel/functions`

##### `next-request-geo-ip`

```bash filename="Terminal"
npx @next/codemod@latest next-request-geo-ip .
```

This codemod installs `@vercel/functions` and transforms `geo` and `ip` properties of `NextRequest` with corresponding `@vercel/functions` features.

For example:

```ts
import type { NextRequest } from 'next/server'

export function GET(req: NextRequest) {
const { geo, ip } = req
}
```

Transforms into:

```ts
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

#### Migrate `ImageResponse` imports
Expand Down
11 changes: 10 additions & 1 deletion packages/next-codemod/bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import path from 'path'
import execa from 'execa'
import { yellow } from 'picocolors'
import isGitClean from 'is-git-clean'
import { uninstallPackage } from '../lib/uninstall-package'
import { installPackage, uninstallPackage } from '../lib/handle-package'

export const jscodeshiftExecutable = require.resolve('.bin/jscodeshift')
export const transformerDirectory = path.join(__dirname, '../', 'transforms')
Expand Down Expand Up @@ -108,6 +108,11 @@ export function runTransform({ files, flags, transformer }) {
)
}
}

if (!dry && transformer === 'next-request-geo-ip') {
console.log('Installing `@vercel/functions`...')
installPackage('@vercel/functions')
}
}

const TRANSFORMER_INQUIRER_CHOICES = [
Expand Down Expand Up @@ -159,6 +164,10 @@ const TRANSFORMER_INQUIRER_CHOICES = [
name: 'built-in-next-font: Uninstall `@next/font` and transform imports to `next/font`',
value: 'built-in-next-font',
},
{
name: 'next-request-geo-ip: Install `@vercel/functions` to replace `geo` and `ip` properties on `NextRequest`',
value: 'next-request-geo-ip',
},
]

function expandFilePathsIfNeeded(filesBeforeExpansion) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,17 @@ export function uninstallPackage(packageToUninstall: string) {

execa.sync(pkgManager, [command, packageToUninstall], { stdio: 'inherit' })
}

export function installPackage(packageToInstall: string) {
const pkgManager = getPkgManager(process.cwd())
if (!pkgManager) throw new Error('Failed to find package manager')

try {
execa.sync(pkgManager, ['add', packageToInstall], { stdio: 'inherit' })
} catch (error) {
throw new Error(
`Failed to install "${packageToInstall}". Please install it manually.`,
{ cause: error }
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

export function GET(request: NextRequest) {
const { buildId, geo: geoAlias, ip: ipAlias } = request;
return NextResponse.json({ buildId, geo: geoAlias, ip: ipAlias });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

import { geolocation, ipAddress } from "@vercel/functions";

export function GET(request: NextRequest) {
const {
buildId
} = request;
const ipAlias = ipAddress(request);
const geoAlias = geolocation(request);
return NextResponse.json({ buildId, geo: geoAlias, ip: ipAlias });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

export function GET(request: NextRequest) {
const { buildId, geo, ip } = request;
return NextResponse.json({ buildId, geo, ip });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

import { geolocation, ipAddress } from "@vercel/functions";

export function GET(request: NextRequest) {
const {
buildId
} = request;
const ip = ipAddress(request);
const geo = geolocation(request);
return NextResponse.json({ buildId, geo, ip });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

export function GET(request: NextRequest) {
const geo = request.geo
const ip = request.ip
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

import { geolocation, ipAddress } from "@vercel/functions";

export function GET(request: NextRequest) {
const geo = geolocation(request)
const ip = ipAddress(request)
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

// for duplicate identifier testing
function ipAddress1() {
return "127.0.0.1";
}
const ipAddress2 = "127.0.0.1";

export function GET(request: NextRequest) {
const ipAddress = request.ip;
return NextResponse.json({ ipAddress });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

import { ipAddress as ipAddress3 } from "@vercel/functions";

// for duplicate identifier testing
function ipAddress1() {
return "127.0.0.1";
}
const ipAddress2 = "127.0.0.1";

export function GET(request: NextRequest) {
const ipAddress = ipAddress3(request);
return NextResponse.json({ ipAddress });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

function getGeo(request: NextRequest) {
return request.geo;
}

function getIp(request: NextRequest) {
return request.ip;
}

export function GET(request: NextRequest) {
const geo = getGeo(request);
const ip = getIp(request);
return NextResponse.json({ geo, ip });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

import { geolocation, ipAddress } from "@vercel/functions";

function getGeo(request: NextRequest) {
return geolocation(request);
}

function getIp(request: NextRequest) {
return ipAddress(request);
}

export function GET(request: NextRequest) {
const geo = getGeo(request);
const ip = getIp(request);
return NextResponse.json({ geo, ip });
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

export function GET(request: NextRequest) {
const { city, country, region } = request.geo;
const latitude = request.geo.latitude;
const longitude = request.geo.longitude;
return NextResponse.json({ city, country, region, latitude, longitude });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

import { geolocation } from "@vercel/functions";

export function GET(request: NextRequest) {
const { city, country, region } = geolocation(request);
const latitude = geolocation(request).latitude;
const longitude = geolocation(request).longitude;
return NextResponse.json({ city, country, region, latitude, longitude });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

export function GET(request: NextRequest) {
const geo = request.geo as NextRequest['geo']
const ip = request.ip
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

import { geolocation, ipAddress, type Geo } from "@vercel/functions";

export function GET(request: NextRequest) {
const geo = geolocation(request) as Geo
const ip = ipAddress(request)
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";
import { geolocation as geo, ipAddress as ip, Geo as GeoType } from "@vercel/functions";

export function GET(request: NextRequest) {
const geo = request.geo as GeoType
const ip = request.ip
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";
import { geolocation as geo, ipAddress as ip, Geo as GeoType } from "@vercel/functions";

export function GET(request: NextRequest) {
const geo = geo(request) as GeoType
const ip = ip(request)
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";
import { geolocation, ipAddress, Geo } from "@vercel/functions";

export function GET(request: NextRequest) {
const geo = request.geo as Geo
const ip = request.ip
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";
import { geolocation, ipAddress, Geo } from "@vercel/functions";

export function GET(request: NextRequest) {
const geo = geolocation(request) as Geo
const ip = ipAddress(request)
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";
import { waitUntil } from "@vercel/functions";

export function GET(request: NextRequest) {
waitUntil(Promise.resolve())
const geo = request.geo as NextRequest['geo']
const ip = request.ip
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";
import { waitUntil, geolocation, ipAddress, type Geo } from "@vercel/functions";

export function GET(request: NextRequest) {
waitUntil(Promise.resolve())
const geo = geolocation(request) as Geo
const ip = ipAddress(request)
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @ts-nocheck
import Link from "next/link";
// should added RIGHT BELOW the FIRST "next/server" import
import type { NextRequest } from "next/server";
import Script from "next/script";
import { notFound } from "next/navigation";
import { NextResponse } from "next/server";

export function GET(request: NextRequest) {
const geo = request.geo
const ip = request.ip
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @ts-nocheck
import Link from "next/link";
// should added RIGHT BELOW the FIRST "next/server" import
import type { NextRequest } from "next/server";
import { geolocation, ipAddress } from "@vercel/functions";
import Script from "next/script";
import { notFound } from "next/navigation";
import { NextResponse } from "next/server";

export function GET(request: NextRequest) {
const geo = geolocation(request)
const ip = ipAddress(request)
return NextResponse.json({ geo, ip })
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

const geo = {
city: 'London',
country: 'United Kingdom',
latitude: 51.5074,
longitude: -0.1278,
region: 'England',
timezone: 'Europe/London',
} satisfies NextRequest['geo']
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

import type { Geo } from "@vercel/functions";

const geo = {
city: 'London',
country: 'United Kingdom',
latitude: 51.5074,
longitude: -0.1278,
region: 'England',
timezone: 'Europe/London',
} satisfies Geo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @ts-nocheck
import { type NextRequest, NextResponse } from "next/server";

const ip = '127.0.0.1' as NextRequest['ip']
Loading

0 comments on commit 0090faa

Please sign in to comment.