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()
+}