From d134c9466a323257bb377ec118b8a94ad709def6 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 14 Dec 2025 15:12:36 -0500 Subject: [PATCH 01/10] feat(magic-redirect): migrate magic-redirect to codemod.com --- package-lock.json | 30 ++++++++ package.json | 5 +- recipes/magic-redirect/README.md | 45 ++++++++++++ recipes/magic-redirect/codemod.yaml | 24 +++++++ recipes/magic-redirect/package.json | 22 ++++++ recipes/magic-redirect/src/workflow.ts | 69 +++++++++++++++++++ .../magic-redirect/tests/expected/location.ts | 33 +++++++++ .../magic-redirect/tests/expected/redirect.ts | 20 ++++++ .../magic-redirect/tests/input/location.ts | 33 +++++++++ .../magic-redirect/tests/input/redirect.ts | 20 ++++++ recipes/magic-redirect/workflow.yaml | 27 ++++++++ tsconfig.json | 2 +- 12 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 recipes/magic-redirect/README.md create mode 100644 recipes/magic-redirect/codemod.yaml create mode 100644 recipes/magic-redirect/package.json create mode 100644 recipes/magic-redirect/src/workflow.ts create mode 100644 recipes/magic-redirect/tests/expected/location.ts create mode 100644 recipes/magic-redirect/tests/expected/redirect.ts create mode 100644 recipes/magic-redirect/tests/input/location.ts create mode 100644 recipes/magic-redirect/tests/input/redirect.ts create mode 100644 recipes/magic-redirect/workflow.yaml diff --git a/package-lock.json b/package-lock.json index dc492f1..d74b3c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@expressjs/codemod", "version": "0.0.5", "license": "MIT", + "workspaces": [ + "./recipes/*" + ], "dependencies": { "commander": "^12.1.0", "fast-glob": "^3.3.2", @@ -30,6 +33,10 @@ }, "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/@ampproject/remapping": { @@ -73,6 +80,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -981,6 +989,17 @@ "node": ">=14.21.3" } }, + "node_modules/@codemod.com/jssg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@codemod.com/jssg-types/-/jssg-types-1.3.1.tgz", + "integrity": "sha512-poYNa8mfr8+4+kBPc3bAKBTaUtOQdg5z3voeGGAAr0tiTBvC4cmmoY/dyHXEWT8F+p8A1tWUnhmJZ4WQXV3HVA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@expressjs/magic-redirect": { + "resolved": "recipes/magic-redirect", + "link": true + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1810,6 +1829,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001669", "electron-to-chromium": "^1.5.41", @@ -2923,6 +2943,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -4622,6 +4643,7 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4804,6 +4826,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "recipes/magic-redirect": { + "name": "@expressjs/magic-redirect", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@codemod.com/jssg-types": "^1.3.0" + } } } } diff --git a/package.json b/package.json index 5c1c94a..ba65d8d 100644 --- a/package.json +++ b/package.json @@ -46,5 +46,8 @@ }, "engines": { "node": ">=18" - } + }, + "workspaces": [ + "./recipes/*" + ] } diff --git a/recipes/magic-redirect/README.md b/recipes/magic-redirect/README.md new file mode 100644 index 0000000..9896f98 --- /dev/null +++ b/recipes/magic-redirect/README.md @@ -0,0 +1,45 @@ +# Migrate legacy `res.redirect('back')` and `res.location('back')` + +Migrates usage of the legacy APIs `res.redirect('back')` and `res.location('back')` to use the recommended approach of accessing the `Referer` header directly from the request object. + +## Example + +### Migrating `res.redirect('back')` + +#### Before + +```js +app.get('/some-route', (req, res) => { + // Some logic here + res.redirect('back'); +}); +``` + +#### After + +```js +app.get('/some-route', (req, res) => { + // Some logic here + res.redirect(req.get('Referer') || '/'); +}); +``` + +### Migrating `res.location('back')` + +#### Before + +```js +app.get('/some-route', (req, res) => { + // Some logic here + res.location('back'); +}); +``` + +#### After + +```js +app.get('/some-route', (req, res) => { + // Some logic here + res.location(req.get('Referer') || '/'); +}); +``` \ No newline at end of file diff --git a/recipes/magic-redirect/codemod.yaml b/recipes/magic-redirect/codemod.yaml new file mode 100644 index 0000000..cec8fcc --- /dev/null +++ b/recipes/magic-redirect/codemod.yaml @@ -0,0 +1,24 @@ +schema_version: "1.0" +name: "@expressjs/magic-redirect" +version: "1.0.0" +description: Migrates usage of the legacy APIs `res.redirect('back')` and `res.location('back')` to the current recommended approaches +author: bjohansebas (Sebastian Beltran) +license: MIT +workflow: workflow.yaml +category: migration + +targets: + languages: + - javascript + - typescript + +keywords: + - transformation + - migration + - express + - redirect + - location + +registry: + access: public + visibility: public \ No newline at end of file diff --git a/recipes/magic-redirect/package.json b/recipes/magic-redirect/package.json new file mode 100644 index 0000000..77dbc6d --- /dev/null +++ b/recipes/magic-redirect/package.json @@ -0,0 +1,22 @@ +{ + "name": "@expressjs/magic-redirect", + "private": true, + "version": "1.0.0", + "description": "Migrates usage of the legacy APIs `res.redirect('back')` and `res.location('back')`.", + "type": "module", + "scripts": { + "test": "npx codemod jssg test -l typescript ./src/workflow.ts ./" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/expressjs/codemod.git", + "directory": "recipes/magic-redirect", + "bugs": "https://github.com/expressjs/codemod/issues" + }, + "author": "bjohansebas (Sebastian Beltran)", + "license": "MIT", + "homepage": "https://github.com/expressjs/codemod/blob/main/recipes/magic-redirect/README.md", + "devDependencies": { + "@codemod.com/jssg-types": "^1.3.1" + } +} \ No newline at end of file diff --git a/recipes/magic-redirect/src/workflow.ts b/recipes/magic-redirect/src/workflow.ts new file mode 100644 index 0000000..240aef4 --- /dev/null +++ b/recipes/magic-redirect/src/workflow.ts @@ -0,0 +1,69 @@ +import type { SgRoot } from "@codemod.com/jssg-types/src/main"; +import type Js from '@codemod.com/jssg-types/src/langs/javascript'; + +async function transform(root: SgRoot): Promise { + const rootNode = root.root(); + + // Helper function to find the request parameter name + function findRequestParamName(node: any): string { + // Start from the call expression and traverse up to find function parameters + let current = node; + while (current) { + const parent = current.parent(); + if (!parent) break; + // Check if we're in a function declaration or arrow function + if (parent.kind() === 'function_declaration' || parent.kind() === 'arrow_function') { + const params = parent.field('parameters'); + + if (params && params.children().length > 0) { + const firstParam = params.children()[1]; + if (firstParam.kind() === 'required_parameter') { + const pattern = firstParam.field('pattern'); + if (pattern && pattern.kind() === 'identifier') { + return pattern.text(); + } + } + } + } + + current = parent; + } + return 'req'; // default fallback + } + + // Find all redirect and location + const nodes = rootNode.findAll({ + rule: { + any: [ + { + pattern: "$OBJ.redirect($ARG)", + }, + { + pattern: "$OBJ.location($ARG)", + }] + } + }); + + const edits = nodes.reduce((acc: any[], node: any) => { + const requestParamName = findRequestParamName(node); + const obj = node.getMatch("OBJ"); + const arg = node.getMatch("ARG"); + + // Only transform when the argument is the literal 'back' (single or double quotes) + const argText = arg && typeof arg.text === 'function' ? arg.text() : null; + if (argText !== "'back'" && argText !== '"back"' && argText !== "‘back’" && argText !== "“back”") { + return acc; // skip this node, no edit + } + + // Case: obj.redirect('back') or obj.location('back') + const objText = obj?.text(); + const methodName = node.text().includes('.redirect(') ? 'redirect' : 'location'; + acc.push(node.replace(`${objText}.${methodName}(${requestParamName}.get("Referrer") || "/")`)); + return acc; + }, [] as any[]); + + const newSource = rootNode.commitEdits(edits); + return newSource; +} + +export default transform; \ No newline at end of file diff --git a/recipes/magic-redirect/tests/expected/location.ts b/recipes/magic-redirect/tests/expected/location.ts new file mode 100644 index 0000000..569856e --- /dev/null +++ b/recipes/magic-redirect/tests/expected/location.ts @@ -0,0 +1,33 @@ +import express from "express"; +import { location } from "somelibrary"; + +const app = express(); + +app.get("/", function (req, res) { + res.location(req.get("Referrer") || "/"); +}); +app.get("/", (req, res) => { + res.location(req.get("Referrer") || "/"); +}); +app.get("/", (req, res) => { + res.location("testing"); +}); +app.get("/", (req, res) => { + res.location(); +}); +app.get("/articles", function (request, response) { + response.location(request.get("Referrer") || "/"); +}); +app.get("/articles", function (request, response) { + response.location("testing"); +}); +app.get("/articles", (request, response) => { + response.location(request.get("Referrer") || "/"); +}); +app.get("/articles", function (_req, _res) { + location("back"); +}); + +export function handleLocation(req, res) { + res.location(req.get("Referrer") || "/"); +} \ No newline at end of file diff --git a/recipes/magic-redirect/tests/expected/redirect.ts b/recipes/magic-redirect/tests/expected/redirect.ts new file mode 100644 index 0000000..b45f88e --- /dev/null +++ b/recipes/magic-redirect/tests/expected/redirect.ts @@ -0,0 +1,20 @@ +import express from "express"; +import { redirect } from "somelibrary"; + +const app = express(); + +app.get("/", function (req, res) { + res.redirect(req.get("Referrer") || "/"); +}); +app.get("/", (req, res) => { + res.redirect(req.get("Referrer") || "/"); +}); +app.get("/articles", function (request, response) { + response.redirect(request.get("Referrer") || "/"); +}); +app.get("/articles", (request, response) => { + response.redirect(request.get("Referrer") || "/"); +}); +app.get("/articles", function (_req, _res) { + redirect("back"); +}); diff --git a/recipes/magic-redirect/tests/input/location.ts b/recipes/magic-redirect/tests/input/location.ts new file mode 100644 index 0000000..0c69ddf --- /dev/null +++ b/recipes/magic-redirect/tests/input/location.ts @@ -0,0 +1,33 @@ +import express from "express"; +import { location } from "somelibrary"; + +const app = express(); + +app.get("/", function (req, res) { + res.location('back'); +}); +app.get("/", (req, res) => { + res.location("back"); +}); +app.get("/", (req, res) => { + res.location("testing"); +}); +app.get("/", (req, res) => { + res.location(); +}); +app.get("/articles", function (request, response) { + response.location("back"); +}); +app.get("/articles", function (request, response) { + response.location("testing"); +}); +app.get("/articles", (request, response) => { + response.location("back"); +}); +app.get("/articles", function (_req, _res) { + location("back"); +}); + +export function handleLocation(req, res) { + res.location('back'); +} \ No newline at end of file diff --git a/recipes/magic-redirect/tests/input/redirect.ts b/recipes/magic-redirect/tests/input/redirect.ts new file mode 100644 index 0000000..7be5724 --- /dev/null +++ b/recipes/magic-redirect/tests/input/redirect.ts @@ -0,0 +1,20 @@ +import express from "express"; +import { redirect } from "somelibrary"; + +const app = express(); + +app.get("/", function (req, res) { + res.redirect("back"); +}); +app.get("/", (req, res) => { + res.redirect("back"); +}); +app.get("/articles", function (request, response) { + response.redirect("back"); +}); +app.get("/articles", (request, response) => { + response.redirect("back"); +}); +app.get("/articles", function (_req, _res) { + redirect("back"); +}); diff --git a/recipes/magic-redirect/workflow.yaml b/recipes/magic-redirect/workflow.yaml new file mode 100644 index 0000000..ddecea1 --- /dev/null +++ b/recipes/magic-redirect/workflow.yaml @@ -0,0 +1,27 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/codemod-com/codemod/refs/heads/main/schemas/workflow.json + +version: "1" + +nodes: + - id: apply-transforms + name: Apply AST Transformations + type: automatic + runtime: + type: direct + steps: + - name: Migrates usage of the legacy APIs `res.redirect('back')` and `res.location('back')` to the current recommended approaches + js-ast-grep: + js_file: src/workflow.ts + base_path: . + include: + - "**/*.cjs" + - "**/*.js" + - "**/*.jsx" + - "**/*.mjs" + - "**/*.cts" + - "**/*.mts" + - "**/*.ts" + - "**/*.tsx" + exclude: + - "**/node_modules/**" + language: typescript \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 8e54036..b06cc81 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,6 @@ "strictNullChecks": true, "outDir": "build" }, - "include": ["**/*.ts"], + "include": ["**/*.ts", "./recipes/",], "exclude": ["node_modules", "build", "**/__testfixtures__", "**/__test__", "**/*.spec.ts"] } From ced14f3d3c6f3fa607b1919df5089bbc89b7dca6 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 14 Dec 2025 15:17:38 -0500 Subject: [PATCH 02/10] fix lint --- biome.json | 5 +- package.json | 4 +- recipes/magic-redirect/package.json | 2 +- recipes/magic-redirect/src/workflow.ts | 65 +++++++++++++------------- tsconfig.json | 2 +- 5 files changed, 40 insertions(+), 38 deletions(-) diff --git a/biome.json b/biome.json index 5531f5c..9aee26d 100644 --- a/biome.json +++ b/biome.json @@ -6,7 +6,7 @@ "useIgnoreFile": true }, "files": { - "ignore": ["__testfixtures__"] + "ignore": ["__testfixtures__", "tests"] }, "linter": { "enabled": true, @@ -15,6 +15,9 @@ "correctness": { "noUnusedImports": "error", "useExhaustiveDependencies": "off" + }, + "suspicious": { + "noExplicitAny": "off" } } }, diff --git a/package.json b/package.json index ba65d8d..d5d5978 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,5 @@ "engines": { "node": ">=18" }, - "workspaces": [ - "./recipes/*" - ] + "workspaces": ["./recipes/*"] } diff --git a/recipes/magic-redirect/package.json b/recipes/magic-redirect/package.json index 77dbc6d..8218df0 100644 --- a/recipes/magic-redirect/package.json +++ b/recipes/magic-redirect/package.json @@ -19,4 +19,4 @@ "devDependencies": { "@codemod.com/jssg-types": "^1.3.1" } -} \ No newline at end of file +} diff --git a/recipes/magic-redirect/src/workflow.ts b/recipes/magic-redirect/src/workflow.ts index 240aef4..cdfd816 100644 --- a/recipes/magic-redirect/src/workflow.ts +++ b/recipes/magic-redirect/src/workflow.ts @@ -1,34 +1,34 @@ -import type { SgRoot } from "@codemod.com/jssg-types/src/main"; -import type Js from '@codemod.com/jssg-types/src/langs/javascript'; +import type Js from '@codemod.com/jssg-types/src/langs/javascript' +import type { SgRoot } from '@codemod.com/jssg-types/src/main' async function transform(root: SgRoot): Promise { - const rootNode = root.root(); - + const rootNode = root.root() + // Helper function to find the request parameter name function findRequestParamName(node: any): string { // Start from the call expression and traverse up to find function parameters - let current = node; + let current = node while (current) { - const parent = current.parent(); - if (!parent) break; + const parent = current.parent() + if (!parent) break // Check if we're in a function declaration or arrow function if (parent.kind() === 'function_declaration' || parent.kind() === 'arrow_function') { - const params = parent.field('parameters'); + const params = parent.field('parameters') if (params && params.children().length > 0) { - const firstParam = params.children()[1]; + const firstParam = params.children()[1] if (firstParam.kind() === 'required_parameter') { - const pattern = firstParam.field('pattern'); + const pattern = firstParam.field('pattern') if (pattern && pattern.kind() === 'identifier') { - return pattern.text(); + return pattern.text() } } } } - current = parent; + current = parent } - return 'req'; // default fallback + return 'req' // default fallback } // Find all redirect and location @@ -36,34 +36,35 @@ async function transform(root: SgRoot): Promise { rule: { any: [ { - pattern: "$OBJ.redirect($ARG)", + pattern: '$OBJ.redirect($ARG)', }, { - pattern: "$OBJ.location($ARG)", - }] - } - }); + pattern: '$OBJ.location($ARG)', + }, + ], + }, + }) const edits = nodes.reduce((acc: any[], node: any) => { - const requestParamName = findRequestParamName(node); - const obj = node.getMatch("OBJ"); - const arg = node.getMatch("ARG"); + const requestParamName = findRequestParamName(node) + const obj = node.getMatch('OBJ') + const arg = node.getMatch('ARG') // Only transform when the argument is the literal 'back' (single or double quotes) - const argText = arg && typeof arg.text === 'function' ? arg.text() : null; - if (argText !== "'back'" && argText !== '"back"' && argText !== "‘back’" && argText !== "“back”") { - return acc; // skip this node, no edit + const argText = arg && typeof arg.text === 'function' ? arg.text() : null + if (argText !== "'back'" && argText !== '"back"' && argText !== '‘back’' && argText !== '“back”') { + return acc // skip this node, no edit } // Case: obj.redirect('back') or obj.location('back') - const objText = obj?.text(); - const methodName = node.text().includes('.redirect(') ? 'redirect' : 'location'; - acc.push(node.replace(`${objText}.${methodName}(${requestParamName}.get("Referrer") || "/")`)); - return acc; - }, [] as any[]); + const objText = obj?.text() + const methodName = node.text().includes('.redirect(') ? 'redirect' : 'location' + acc.push(node.replace(`${objText}.${methodName}(${requestParamName}.get("Referrer") || "/")`)) + return acc + }, [] as any[]) - const newSource = rootNode.commitEdits(edits); - return newSource; + const newSource = rootNode.commitEdits(edits) + return newSource } -export default transform; \ No newline at end of file +export default transform diff --git a/tsconfig.json b/tsconfig.json index b06cc81..725d5f2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,6 @@ "strictNullChecks": true, "outDir": "build" }, - "include": ["**/*.ts", "./recipes/",], + "include": ["**/*.ts", "./recipes/"], "exclude": ["node_modules", "build", "**/__testfixtures__", "**/__test__", "**/*.spec.ts"] } From fc04f729a9d6dea47ba47534f5274bcd8154c4cb Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 14 Dec 2025 17:43:31 -0500 Subject: [PATCH 03/10] feat(tests): update test commands and add legacy test support --- .github/workflows/ci.yml | 9 +++++++-- package.json | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27537c1..872b0c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,7 +71,12 @@ jobs: echo "Node.js version: $(node -v)" echo "NPM version: $(npm -v)" - - name: Run tests + - name: Run tests-legacy cli shell: bash run: | - npm run test:ci \ No newline at end of file + npm run test-legacy:ci + + - name: Run test + shell: bash + run: | + npm run test \ No newline at end of file diff --git a/package.json b/package.json index d5d5978..21d2839 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,9 @@ "build": "tsc -d -p tsconfig.json", "lint": "biome check", "lint:fix": "biome check --fix", - "test": "jest", - "test:ci": "jest --ci", + "test": "npm run test --workspaces --if-present", + "test-legacy": "jest", + "test-legacy:ci": "jest --ci", "prepublishOnly": "npm run clean && npm run build" }, "dependencies": { From 716d99e5bc8d755002c15a4a40eda9535f4182ea Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 14 Dec 2025 18:37:43 -0500 Subject: [PATCH 04/10] feat(redirect): enhance redirect handlers with additional test cases --- recipes/magic-redirect/src/workflow.ts | 39 ++++++++++++++----- .../magic-redirect/tests/expected/redirect.ts | 21 ++++++++++ .../magic-redirect/tests/input/redirect.ts | 21 ++++++++++ 3 files changed, 71 insertions(+), 10 deletions(-) diff --git a/recipes/magic-redirect/src/workflow.ts b/recipes/magic-redirect/src/workflow.ts index cdfd816..4a71241 100644 --- a/recipes/magic-redirect/src/workflow.ts +++ b/recipes/magic-redirect/src/workflow.ts @@ -6,21 +6,39 @@ async function transform(root: SgRoot): Promise { // Helper function to find the request parameter name function findRequestParamName(node: any): string { - // Start from the call expression and traverse up to find function parameters let current = node + const funcKinds = new Set([ + 'function_declaration', + 'function_expression', + 'function', + 'arrow_function', + 'method_definition', + ]) + while (current) { const parent = current.parent() if (!parent) break - // Check if we're in a function declaration or arrow function - if (parent.kind() === 'function_declaration' || parent.kind() === 'arrow_function') { - const params = parent.field('parameters') - if (params && params.children().length > 0) { - const firstParam = params.children()[1] - if (firstParam.kind() === 'required_parameter') { - const pattern = firstParam.field('pattern') - if (pattern && pattern.kind() === 'identifier') { - return pattern.text() + const kind = parent?.kind() + if (kind && funcKinds.has(kind)) { + const candidateFields = ['parameters', 'parameter', 'formal_parameters', 'params'] + let params: any = null + + for (const f of candidateFields) { + params = parent?.field(f) + if (params) break + } + + if (params) { + const children = typeof params.children === 'function' ? params.children() : [] + if (children && children.length > 0) { + const first = children[1] + + if (first?.kind() === 'required_parameter') { + const pattern = first?.field('pattern') + if (pattern && typeof pattern.kind === 'function' && pattern.kind() === 'identifier') { + return pattern.text() + } } } } @@ -28,6 +46,7 @@ async function transform(root: SgRoot): Promise { current = parent } + return 'req' // default fallback } diff --git a/recipes/magic-redirect/tests/expected/redirect.ts b/recipes/magic-redirect/tests/expected/redirect.ts index b45f88e..18386c0 100644 --- a/recipes/magic-redirect/tests/expected/redirect.ts +++ b/recipes/magic-redirect/tests/expected/redirect.ts @@ -9,12 +9,33 @@ app.get("/", function (req, res) { app.get("/", (req, res) => { res.redirect(req.get("Referrer") || "/"); }); +app.get("/", (req, res) => { + res.redirect("testing"); +}); +app.get("/", (req, res) => { + res.redirect(); +}); app.get("/articles", function (request, response) { response.redirect(request.get("Referrer") || "/"); }); app.get("/articles", (request, response) => { response.redirect(request.get("Referrer") || "/"); }); +app.get("/articles", function (request, response) { + response.redirect("testing"); +}); app.get("/articles", function (_req, _res) { redirect("back"); }); + +export function handler(requests, response) { + response.redirect(requests.get("Referrer") || "/"); +} + +export function handleRedirect(req: any) { + req.redirect(req.get("Referrer") || "/"); +} + +export function handlerWith(req: any, res: any) { + res.redirect(req.get("Referrer") || "/"); +} \ No newline at end of file diff --git a/recipes/magic-redirect/tests/input/redirect.ts b/recipes/magic-redirect/tests/input/redirect.ts index 7be5724..650ec7c 100644 --- a/recipes/magic-redirect/tests/input/redirect.ts +++ b/recipes/magic-redirect/tests/input/redirect.ts @@ -9,12 +9,33 @@ app.get("/", function (req, res) { app.get("/", (req, res) => { res.redirect("back"); }); +app.get("/", (req, res) => { + res.redirect("testing"); +}); +app.get("/", (req, res) => { + res.redirect(); +}); app.get("/articles", function (request, response) { response.redirect("back"); }); app.get("/articles", (request, response) => { response.redirect("back"); }); +app.get("/articles", function (request, response) { + response.redirect("testing"); +}); app.get("/articles", function (_req, _res) { redirect("back"); }); + +export function handler(requests, response) { + response.redirect('back'); +} + +export function handleRedirect(req: any) { + req.redirect('back'); +} + +export function handlerWith(req: any, res: any) { + res.redirect('back'); +} \ No newline at end of file From c9603ca128479ac33635fd0dc5a6ba32e0589ef4 Mon Sep 17 00:00:00 2001 From: Mohamad Mohebifar Date: Sun, 14 Dec 2025 16:53:56 -0800 Subject: [PATCH 05/10] refactor: refactor the magic-redirect-codemod (#92) * refactor: refactor the express codemod * fix lint Signed-off-by: Sebastian Beltran --------- Signed-off-by: Sebastian Beltran Co-authored-by: Sebastian Beltran --- package-lock.json | 6 +- recipes/magic-redirect/src/workflow.ts | 129 ++++++++++--------------- recipes/magic-redirect/workflow.yaml | 1 + 3 files changed, 55 insertions(+), 81 deletions(-) diff --git a/package-lock.json b/package-lock.json index d74b3c9..c125cc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -80,7 +80,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -1829,7 +1828,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001669", "electron-to-chromium": "^1.5.41", @@ -2943,7 +2941,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -4643,7 +4640,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4832,7 +4828,7 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@codemod.com/jssg-types": "^1.3.0" + "@codemod.com/jssg-types": "^1.3.1" } } } diff --git a/recipes/magic-redirect/src/workflow.ts b/recipes/magic-redirect/src/workflow.ts index 4a71241..1364b5c 100644 --- a/recipes/magic-redirect/src/workflow.ts +++ b/recipes/magic-redirect/src/workflow.ts @@ -1,89 +1,66 @@ import type Js from '@codemod.com/jssg-types/src/langs/javascript' -import type { SgRoot } from '@codemod.com/jssg-types/src/main' +import type { Edit, SgNode, SgRoot } from '@codemod.com/jssg-types/src/main' -async function transform(root: SgRoot): Promise { - const rootNode = root.root() +function getStringLiteralValue(node: SgNode): string | null { + if (!node.is('string')) return null + + const fragments = node.findAll({ rule: { kind: 'string_fragment' } }) + if (fragments.length !== 1) return null + return fragments[0]?.text() ?? null +} - // Helper function to find the request parameter name - function findRequestParamName(node: any): string { - let current = node - const funcKinds = new Set([ - 'function_declaration', - 'function_expression', - 'function', - 'arrow_function', - 'method_definition', - ]) - - while (current) { - const parent = current.parent() - if (!parent) break - - const kind = parent?.kind() - if (kind && funcKinds.has(kind)) { - const candidateFields = ['parameters', 'parameter', 'formal_parameters', 'params'] - let params: any = null - - for (const f of candidateFields) { - params = parent?.field(f) - if (params) break - } - - if (params) { - const children = typeof params.children === 'function' ? params.children() : [] - if (children && children.length > 0) { - const first = children[1] - - if (first?.kind() === 'required_parameter') { - const pattern = first?.field('pattern') - if (pattern && typeof pattern.kind === 'function' && pattern.kind() === 'identifier') { - return pattern.text() - } - } - } - } - } - - current = parent - } - - return 'req' // default fallback +function findParentFunctionParameters(node: SgNode): SgNode | null { + let parent = node.parent() + while (parent) { + if (parent.is('formal_parameters')) return parent + parent = parent.parent() } + return null +} + +async function transform(root: SgRoot): Promise { + const rootNode = root.root() - // Find all redirect and location const nodes = rootNode.findAll({ rule: { - any: [ - { - pattern: '$OBJ.redirect($ARG)', - }, - { - pattern: '$OBJ.location($ARG)', - }, - ], + pattern: '$OBJ.$METHOD($ARG)', + }, + constraints: { + METHOD: { regex: '^(redirect|location)$' }, + ARG: { pattern: { context: "'back'", strictness: 'relaxed' } }, }, }) - const edits = nodes.reduce((acc: any[], node: any) => { - const requestParamName = findRequestParamName(node) - const obj = node.getMatch('OBJ') - const arg = node.getMatch('ARG') - - // Only transform when the argument is the literal 'back' (single or double quotes) - const argText = arg && typeof arg.text === 'function' ? arg.text() : null - if (argText !== "'back'" && argText !== '"back"' && argText !== '‘back’' && argText !== '“back”') { - return acc // skip this node, no edit - } - - // Case: obj.redirect('back') or obj.location('back') - const objText = obj?.text() - const methodName = node.text().includes('.redirect(') ? 'redirect' : 'location' - acc.push(node.replace(`${objText}.${methodName}(${requestParamName}.get("Referrer") || "/")`)) - return acc - }, [] as any[]) - - const newSource = rootNode.commitEdits(edits) - return newSource + const edits: Edit[] = [] + + for (const call of nodes) { + const arg = call.getMatch('ARG') + const obj = call.getMatch('OBJ') + if (!arg || !obj) continue + + if (getStringLiteralValue(arg) !== 'back') continue + + const objDef = obj.definition({ resolveExternal: false }) + if (!objDef) continue + + const isParameter = objDef.node.matches({ + rule: { inside: { kind: 'formal_parameters', stopBy: 'end' } }, + }) + if (!isParameter) continue + + const parameters = findParentFunctionParameters(objDef.node) + if (!parameters) continue + + const firstParameter = parameters.find({ rule: { kind: 'identifier' } }) + if (!firstParameter) continue + + const requestName = firstParameter.text() + + edits.push(arg.replace(`${requestName}.get("Referrer") || "/"`)) + } + + if (edits.length === 0) return null + return rootNode.commitEdits(edits) } export default transform diff --git a/recipes/magic-redirect/workflow.yaml b/recipes/magic-redirect/workflow.yaml index ddecea1..15bb5c2 100644 --- a/recipes/magic-redirect/workflow.yaml +++ b/recipes/magic-redirect/workflow.yaml @@ -13,6 +13,7 @@ nodes: js-ast-grep: js_file: src/workflow.ts base_path: . + semantic_analysis: file include: - "**/*.cjs" - "**/*.js" From 757fd3d4066825854b75fff985ecae4f91b12237 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Tue, 16 Dec 2025 15:16:18 -0500 Subject: [PATCH 06/10] docs: improve documentation --- recipes/magic-redirect/README.md | 38 +++++++++++++------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/recipes/magic-redirect/README.md b/recipes/magic-redirect/README.md index 9896f98..26892ba 100644 --- a/recipes/magic-redirect/README.md +++ b/recipes/magic-redirect/README.md @@ -1,45 +1,37 @@ # Migrate legacy `res.redirect('back')` and `res.location('back')` -Migrates usage of the legacy APIs `res.redirect('back')` and `res.location('back')` to use the recommended approach of accessing the `Referer` header directly from the request object. +Migrates usage of the legacy APIs `res.redirect('back')` and `res.location('back')` +to use the recommended approach of accessing the `Referer` header directly from +the request object. Versions of Express before 5 allowed the use of the string +"back" as a shortcut to redirect to the referring page, but this has been +deprecated. ## Example ### Migrating `res.redirect('back')` -#### Before +The migration involves replacing instances of `res.redirect('back')` with `res.redirect(req.get('Referer') || '/')`. -```js +```diff app.get('/some-route', (req, res) => { // Some logic here - res.redirect('back'); -}); -``` - -#### After - -```js -app.get('/some-route', (req, res) => { - // Some logic here - res.redirect(req.get('Referer') || '/'); +- res.redirect('back'); ++ res.redirect(req.get('Referer') || '/'); }); ``` ### Migrating `res.location('back')` -#### Before +The migration involves replacing instances of `res.location('back')` with `res.location(req.get('Referer') || '/')`. -```js +```diff app.get('/some-route', (req, res) => { // Some logic here - res.location('back'); +- res.location('back'); ++ res.location(req.get('Referer') || '/'); }); ``` -#### After +## References -```js -app.get('/some-route', (req, res) => { - // Some logic here - res.location(req.get('Referer') || '/'); -}); -``` \ No newline at end of file +- [Migration of res.redirect('back') and res.location('back')](https://expressjs.com/en/guide/migrating-5.html#magic-redirect) From c7fd8fa2fe62c847d7641ec08dec63bdee7f1ef6 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Tue, 16 Dec 2025 15:17:40 -0500 Subject: [PATCH 07/10] chore: rename folder to codemods --- .github/workflows/publish.yml | 6 +++--- {recipes => codemods}/.gitkeep | 0 {recipes => codemods}/magic-redirect/README.md | 0 {recipes => codemods}/magic-redirect/codemod.yaml | 0 {recipes => codemods}/magic-redirect/package.json | 0 {recipes => codemods}/magic-redirect/src/workflow.ts | 0 .../magic-redirect/tests/expected/location.ts | 0 .../magic-redirect/tests/expected/redirect.ts | 0 .../magic-redirect/tests/input/location.ts | 0 .../magic-redirect/tests/input/redirect.ts | 0 {recipes => codemods}/magic-redirect/workflow.yaml | 0 tsconfig.json | 2 +- 12 files changed, 4 insertions(+), 4 deletions(-) rename {recipes => codemods}/.gitkeep (100%) rename {recipes => codemods}/magic-redirect/README.md (100%) rename {recipes => codemods}/magic-redirect/codemod.yaml (100%) rename {recipes => codemods}/magic-redirect/package.json (100%) rename {recipes => codemods}/magic-redirect/src/workflow.ts (100%) rename {recipes => codemods}/magic-redirect/tests/expected/location.ts (100%) rename {recipes => codemods}/magic-redirect/tests/expected/redirect.ts (100%) rename {recipes => codemods}/magic-redirect/tests/input/location.ts (100%) rename {recipes => codemods}/magic-redirect/tests/input/redirect.ts (100%) rename {recipes => codemods}/magic-redirect/workflow.yaml (100%) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 88353f7..4d2e197 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -59,7 +59,7 @@ jobs: VERSION="${TAG%@*}" # Everything before @ VERSION="${VERSION#v}" # Remove v prefix CODEMOD_NAME="${TAG#*@}" # Everything after @ - CODEMOD_PATH="recipes/$CODEMOD_NAME" + CODEMOD_PATH="codemods/$CODEMOD_NAME" # Set outputs echo "version=$VERSION" >> $GITHUB_OUTPUT @@ -72,8 +72,8 @@ jobs: run: | if [[ ! -d "$CODEMOD_PATH" ]]; then echo "❌ Codemod directory not found: $CODEMOD_PATH" - echo "Available directories in recipes/:" - ls -lah recipes/ || echo "No recipes directory found" + echo "Available directories in codemods/:" + ls -lah codemods/ || echo "No codemods directory found" exit 1 fi diff --git a/recipes/.gitkeep b/codemods/.gitkeep similarity index 100% rename from recipes/.gitkeep rename to codemods/.gitkeep diff --git a/recipes/magic-redirect/README.md b/codemods/magic-redirect/README.md similarity index 100% rename from recipes/magic-redirect/README.md rename to codemods/magic-redirect/README.md diff --git a/recipes/magic-redirect/codemod.yaml b/codemods/magic-redirect/codemod.yaml similarity index 100% rename from recipes/magic-redirect/codemod.yaml rename to codemods/magic-redirect/codemod.yaml diff --git a/recipes/magic-redirect/package.json b/codemods/magic-redirect/package.json similarity index 100% rename from recipes/magic-redirect/package.json rename to codemods/magic-redirect/package.json diff --git a/recipes/magic-redirect/src/workflow.ts b/codemods/magic-redirect/src/workflow.ts similarity index 100% rename from recipes/magic-redirect/src/workflow.ts rename to codemods/magic-redirect/src/workflow.ts diff --git a/recipes/magic-redirect/tests/expected/location.ts b/codemods/magic-redirect/tests/expected/location.ts similarity index 100% rename from recipes/magic-redirect/tests/expected/location.ts rename to codemods/magic-redirect/tests/expected/location.ts diff --git a/recipes/magic-redirect/tests/expected/redirect.ts b/codemods/magic-redirect/tests/expected/redirect.ts similarity index 100% rename from recipes/magic-redirect/tests/expected/redirect.ts rename to codemods/magic-redirect/tests/expected/redirect.ts diff --git a/recipes/magic-redirect/tests/input/location.ts b/codemods/magic-redirect/tests/input/location.ts similarity index 100% rename from recipes/magic-redirect/tests/input/location.ts rename to codemods/magic-redirect/tests/input/location.ts diff --git a/recipes/magic-redirect/tests/input/redirect.ts b/codemods/magic-redirect/tests/input/redirect.ts similarity index 100% rename from recipes/magic-redirect/tests/input/redirect.ts rename to codemods/magic-redirect/tests/input/redirect.ts diff --git a/recipes/magic-redirect/workflow.yaml b/codemods/magic-redirect/workflow.yaml similarity index 100% rename from recipes/magic-redirect/workflow.yaml rename to codemods/magic-redirect/workflow.yaml diff --git a/tsconfig.json b/tsconfig.json index 725d5f2..fbdb66a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,6 @@ "strictNullChecks": true, "outDir": "build" }, - "include": ["**/*.ts", "./recipes/"], + "include": ["**/*.ts", "./codemods/"], "exclude": ["node_modules", "build", "**/__testfixtures__", "**/__test__", "**/*.spec.ts"] } From 76f2d3ec33145f97183d1ef8be99f03eeda8cf46 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Tue, 16 Dec 2025 15:19:30 -0500 Subject: [PATCH 08/10] rename codemod --- codemods/magic-redirect/codemod.yaml | 2 +- codemods/magic-redirect/package.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/codemods/magic-redirect/codemod.yaml b/codemods/magic-redirect/codemod.yaml index cec8fcc..5f606d3 100644 --- a/codemods/magic-redirect/codemod.yaml +++ b/codemods/magic-redirect/codemod.yaml @@ -1,5 +1,5 @@ schema_version: "1.0" -name: "@expressjs/magic-redirect" +name: "@expressjs/back-redirect-deprecated" version: "1.0.0" description: Migrates usage of the legacy APIs `res.redirect('back')` and `res.location('back')` to the current recommended approaches author: bjohansebas (Sebastian Beltran) diff --git a/codemods/magic-redirect/package.json b/codemods/magic-redirect/package.json index 8218df0..83d8feb 100644 --- a/codemods/magic-redirect/package.json +++ b/codemods/magic-redirect/package.json @@ -1,5 +1,5 @@ { - "name": "@expressjs/magic-redirect", + "name": "@expressjs/back-redirect-deprecated", "private": true, "version": "1.0.0", "description": "Migrates usage of the legacy APIs `res.redirect('back')` and `res.location('back')`.", @@ -10,12 +10,12 @@ "repository": { "type": "git", "url": "git+https://github.com/expressjs/codemod.git", - "directory": "recipes/magic-redirect", + "directory": "codemods/back-redirect-deprecated", "bugs": "https://github.com/expressjs/codemod/issues" }, "author": "bjohansebas (Sebastian Beltran)", "license": "MIT", - "homepage": "https://github.com/expressjs/codemod/blob/main/recipes/magic-redirect/README.md", + "homepage": "https://github.com/expressjs/codemod/blob/main/codemods/back-redirect-deprecated/README.md", "devDependencies": { "@codemod.com/jssg-types": "^1.3.1" } From 8e66d9dc91cb7dd374b2c8176098d2982b5063a4 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Tue, 16 Dec 2025 15:21:58 -0500 Subject: [PATCH 09/10] fix: update workspace paths to use codemods directory --- package-lock.json | 19 ++++++++++++++++--- package.json | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c125cc8..01fc31c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.5", "license": "MIT", "workspaces": [ - "./recipes/*" + "./codemods/*" ], "dependencies": { "commander": "^12.1.0", @@ -39,6 +39,14 @@ "url": "https://opencollective.com/express" } }, + "codemods/magic-redirect": { + "name": "@expressjs/back-redirect-deprecated", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@codemod.com/jssg-types": "^1.3.1" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -80,6 +88,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.0", @@ -995,8 +1004,8 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/@expressjs/magic-redirect": { - "resolved": "recipes/magic-redirect", + "node_modules/@expressjs/back-redirect-deprecated": { + "resolved": "codemods/magic-redirect", "link": true }, "node_modules/@istanbuljs/load-nyc-config": { @@ -1828,6 +1837,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001669", "electron-to-chromium": "^1.5.41", @@ -2941,6 +2951,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -4640,6 +4651,7 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4826,6 +4838,7 @@ "recipes/magic-redirect": { "name": "@expressjs/magic-redirect", "version": "1.0.0", + "extraneous": true, "license": "MIT", "devDependencies": { "@codemod.com/jssg-types": "^1.3.1" diff --git a/package.json b/package.json index 21d2839..6ea09fd 100644 --- a/package.json +++ b/package.json @@ -48,5 +48,5 @@ "engines": { "node": ">=18" }, - "workspaces": ["./recipes/*"] + "workspaces": ["./codemods/*"] } From b71ffd221a4d309f73693d5b9df9529883e4c57e Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Tue, 23 Dec 2025 18:57:09 -0500 Subject: [PATCH 10/10] rename folder --- codemods/{magic-redirect => back-redirect-deprecated}/README.md | 0 .../{magic-redirect => back-redirect-deprecated}/codemod.yaml | 0 .../{magic-redirect => back-redirect-deprecated}/package.json | 0 .../{magic-redirect => back-redirect-deprecated}/src/workflow.ts | 0 .../tests/expected/location.ts | 0 .../tests/expected/redirect.ts | 0 .../tests/input/location.ts | 0 .../tests/input/redirect.ts | 0 .../{magic-redirect => back-redirect-deprecated}/workflow.yaml | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename codemods/{magic-redirect => back-redirect-deprecated}/README.md (100%) rename codemods/{magic-redirect => back-redirect-deprecated}/codemod.yaml (100%) rename codemods/{magic-redirect => back-redirect-deprecated}/package.json (100%) rename codemods/{magic-redirect => back-redirect-deprecated}/src/workflow.ts (100%) rename codemods/{magic-redirect => back-redirect-deprecated}/tests/expected/location.ts (100%) rename codemods/{magic-redirect => back-redirect-deprecated}/tests/expected/redirect.ts (100%) rename codemods/{magic-redirect => back-redirect-deprecated}/tests/input/location.ts (100%) rename codemods/{magic-redirect => back-redirect-deprecated}/tests/input/redirect.ts (100%) rename codemods/{magic-redirect => back-redirect-deprecated}/workflow.yaml (100%) diff --git a/codemods/magic-redirect/README.md b/codemods/back-redirect-deprecated/README.md similarity index 100% rename from codemods/magic-redirect/README.md rename to codemods/back-redirect-deprecated/README.md diff --git a/codemods/magic-redirect/codemod.yaml b/codemods/back-redirect-deprecated/codemod.yaml similarity index 100% rename from codemods/magic-redirect/codemod.yaml rename to codemods/back-redirect-deprecated/codemod.yaml diff --git a/codemods/magic-redirect/package.json b/codemods/back-redirect-deprecated/package.json similarity index 100% rename from codemods/magic-redirect/package.json rename to codemods/back-redirect-deprecated/package.json diff --git a/codemods/magic-redirect/src/workflow.ts b/codemods/back-redirect-deprecated/src/workflow.ts similarity index 100% rename from codemods/magic-redirect/src/workflow.ts rename to codemods/back-redirect-deprecated/src/workflow.ts diff --git a/codemods/magic-redirect/tests/expected/location.ts b/codemods/back-redirect-deprecated/tests/expected/location.ts similarity index 100% rename from codemods/magic-redirect/tests/expected/location.ts rename to codemods/back-redirect-deprecated/tests/expected/location.ts diff --git a/codemods/magic-redirect/tests/expected/redirect.ts b/codemods/back-redirect-deprecated/tests/expected/redirect.ts similarity index 100% rename from codemods/magic-redirect/tests/expected/redirect.ts rename to codemods/back-redirect-deprecated/tests/expected/redirect.ts diff --git a/codemods/magic-redirect/tests/input/location.ts b/codemods/back-redirect-deprecated/tests/input/location.ts similarity index 100% rename from codemods/magic-redirect/tests/input/location.ts rename to codemods/back-redirect-deprecated/tests/input/location.ts diff --git a/codemods/magic-redirect/tests/input/redirect.ts b/codemods/back-redirect-deprecated/tests/input/redirect.ts similarity index 100% rename from codemods/magic-redirect/tests/input/redirect.ts rename to codemods/back-redirect-deprecated/tests/input/redirect.ts diff --git a/codemods/magic-redirect/workflow.yaml b/codemods/back-redirect-deprecated/workflow.yaml similarity index 100% rename from codemods/magic-redirect/workflow.yaml rename to codemods/back-redirect-deprecated/workflow.yaml