From 07712a07c2272e14055dd603bae654cb5aeaab08 Mon Sep 17 00:00:00 2001 From: Jiwon Choi Date: Wed, 9 Oct 2024 04:11:23 +0900 Subject: [PATCH] feat(next-codemod): add `app-dir-runtime-config-experimental-edge` (#70968) ### Why? We had breaking change in App Router at https://github.com/vercel/next.js/pull/70480 where it strictly checks `runtime` route segment config. If it is set to `experimental-edge`, will throw. Therefore we support a codemod to replace it to `edge`. --- packages/next-codemod/lib/utils.ts | 6 +++ .../already-edge.input.tsx | 4 ++ .../already-edge.output.tsx | 4 ++ .../basic.input.tsx | 4 ++ .../basic.output.tsx | 4 ++ .../no-runtime.input.tsx | 3 ++ .../no-runtime.output.tsx | 3 ++ .../nodejs.input.tsx | 4 ++ .../nodejs.output.tsx | 4 ++ ...r-runtime-config-experimental-edge.test.js | 14 +++++++ ...pp-dir-runtime-config-experimental-edge.ts | 40 +++++++++++++++++++ 11 files changed, 90 insertions(+) create mode 100644 packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/already-edge.input.tsx create mode 100644 packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/already-edge.output.tsx create mode 100644 packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/basic.input.tsx create mode 100644 packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/basic.output.tsx create mode 100644 packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/no-runtime.input.tsx create mode 100644 packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/no-runtime.output.tsx create mode 100644 packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/nodejs.input.tsx create mode 100644 packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/nodejs.output.tsx create mode 100644 packages/next-codemod/transforms/__tests__/app-dir-runtime-config-experimental-edge.test.js create mode 100644 packages/next-codemod/transforms/app-dir-runtime-config-experimental-edge.ts diff --git a/packages/next-codemod/lib/utils.ts b/packages/next-codemod/lib/utils.ts index 17e40839732868..6308a45a1d5f55 100644 --- a/packages/next-codemod/lib/utils.ts +++ b/packages/next-codemod/lib/utils.ts @@ -116,4 +116,10 @@ export const TRANSFORMER_INQUIRER_CHOICES = [ value: 'next-async-request-api', version: '15.0.0-canary.171', }, + { + title: + 'Transforms `experimental-edge` to `edge` in the `runtime` route segment configuration within the App Router', + value: 'app-dir-runtime-config-experimental-edge', + version: '15.0.0-canary.179', + }, ] diff --git a/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/already-edge.input.tsx b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/already-edge.input.tsx new file mode 100644 index 00000000000000..1087abc543cbb6 --- /dev/null +++ b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/already-edge.input.tsx @@ -0,0 +1,4 @@ +export const runtime = "edge"; +export default function Page() { + return
hello world
; +} \ No newline at end of file diff --git a/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/already-edge.output.tsx b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/already-edge.output.tsx new file mode 100644 index 00000000000000..1087abc543cbb6 --- /dev/null +++ b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/already-edge.output.tsx @@ -0,0 +1,4 @@ +export const runtime = "edge"; +export default function Page() { + return
hello world
; +} \ No newline at end of file diff --git a/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/basic.input.tsx b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/basic.input.tsx new file mode 100644 index 00000000000000..4a82859b7b39ac --- /dev/null +++ b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/basic.input.tsx @@ -0,0 +1,4 @@ +export const runtime = "experimental-edge"; +export default function Page() { + return
hello world
; +} \ No newline at end of file diff --git a/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/basic.output.tsx b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/basic.output.tsx new file mode 100644 index 00000000000000..1087abc543cbb6 --- /dev/null +++ b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/basic.output.tsx @@ -0,0 +1,4 @@ +export const runtime = "edge"; +export default function Page() { + return
hello world
; +} \ No newline at end of file diff --git a/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/no-runtime.input.tsx b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/no-runtime.input.tsx new file mode 100644 index 00000000000000..f5cdf6d2885e40 --- /dev/null +++ b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/no-runtime.input.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return
hello world
; +} \ No newline at end of file diff --git a/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/no-runtime.output.tsx b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/no-runtime.output.tsx new file mode 100644 index 00000000000000..f5cdf6d2885e40 --- /dev/null +++ b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/no-runtime.output.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return
hello world
; +} \ No newline at end of file diff --git a/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/nodejs.input.tsx b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/nodejs.input.tsx new file mode 100644 index 00000000000000..a8900029402860 --- /dev/null +++ b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/nodejs.input.tsx @@ -0,0 +1,4 @@ +export const runtime = "nodejs"; +export default function Page() { + return
hello world
; +} \ No newline at end of file diff --git a/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/nodejs.output.tsx b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/nodejs.output.tsx new file mode 100644 index 00000000000000..a8900029402860 --- /dev/null +++ b/packages/next-codemod/transforms/__testfixtures__/app-dir-runtime-config-experimental-edge/nodejs.output.tsx @@ -0,0 +1,4 @@ +export const runtime = "nodejs"; +export default function Page() { + return
hello world
; +} \ No newline at end of file diff --git a/packages/next-codemod/transforms/__tests__/app-dir-runtime-config-experimental-edge.test.js b/packages/next-codemod/transforms/__tests__/app-dir-runtime-config-experimental-edge.test.js new file mode 100644 index 00000000000000..ab00d7678adefd --- /dev/null +++ b/packages/next-codemod/transforms/__tests__/app-dir-runtime-config-experimental-edge.test.js @@ -0,0 +1,14 @@ +/* global jest */ +jest.autoMockOff() +const defineTest = require('jscodeshift/dist/testUtils').defineTest +const { readdirSync } = require('fs') +const { join } = require('path') +const fixtureDir = 'app-dir-runtime-config-experimental-edge' +const fixtureDirPath = join(__dirname, '..', '__testfixtures__', fixtureDir) +const fixtures = readdirSync(fixtureDirPath) + .filter(file => file.endsWith('.input.tsx')) + .map(file => file.replace('.input.tsx', '')) +for (const fixture of fixtures) { + const prefix = `${fixtureDir}/${fixture}`; + defineTest(__dirname, fixtureDir, null, prefix, { parser: 'tsx' }); +} \ No newline at end of file diff --git a/packages/next-codemod/transforms/app-dir-runtime-config-experimental-edge.ts b/packages/next-codemod/transforms/app-dir-runtime-config-experimental-edge.ts new file mode 100644 index 00000000000000..0cc45cc55a2098 --- /dev/null +++ b/packages/next-codemod/transforms/app-dir-runtime-config-experimental-edge.ts @@ -0,0 +1,40 @@ +import type { API, FileInfo } from 'jscodeshift' + +export default function transformer(file: FileInfo, api: API) { + if ( + process.env.NODE_ENV !== 'test' && + !/[/\\]app[/\\].*?(page|layout|route)\.[^/\\]+$/.test(file.path) + ) { + return file.source + } + + const j = api.jscodeshift.withParser('tsx') + const root = j(file.source) + + const runtimeExport = root.find(j.ExportNamedDeclaration, { + declaration: { + type: 'VariableDeclaration', + declarations: [ + { + id: { name: 'runtime' }, + }, + ], + }, + }) + + if (runtimeExport.size() !== 1) { + return file.source + } + + const runtimeValue = runtimeExport.find(j.StringLiteral, { + value: 'experimental-edge', + }) + + if (runtimeValue.size() !== 1) { + return file.source + } + + runtimeValue.replaceWith(j.stringLiteral('edge')) + + return root.toSource() +}