Skip to content

Commit d134c94

Browse files
committed
feat(magic-redirect): migrate magic-redirect to codemod.com
1 parent 31602e2 commit d134c94

12 files changed

Lines changed: 328 additions & 2 deletions

File tree

package-lock.json

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,8 @@
4646
},
4747
"engines": {
4848
"node": ">=18"
49-
}
49+
},
50+
"workspaces": [
51+
"./recipes/*"
52+
]
5053
}

recipes/magic-redirect/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Migrate legacy `res.redirect('back')` and `res.location('back')`
2+
3+
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.
4+
5+
## Example
6+
7+
### Migrating `res.redirect('back')`
8+
9+
#### Before
10+
11+
```js
12+
app.get('/some-route', (req, res) => {
13+
// Some logic here
14+
res.redirect('back');
15+
});
16+
```
17+
18+
#### After
19+
20+
```js
21+
app.get('/some-route', (req, res) => {
22+
// Some logic here
23+
res.redirect(req.get('Referer') || '/');
24+
});
25+
```
26+
27+
### Migrating `res.location('back')`
28+
29+
#### Before
30+
31+
```js
32+
app.get('/some-route', (req, res) => {
33+
// Some logic here
34+
res.location('back');
35+
});
36+
```
37+
38+
#### After
39+
40+
```js
41+
app.get('/some-route', (req, res) => {
42+
// Some logic here
43+
res.location(req.get('Referer') || '/');
44+
});
45+
```
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
schema_version: "1.0"
2+
name: "@expressjs/magic-redirect"
3+
version: "1.0.0"
4+
description: Migrates usage of the legacy APIs `res.redirect('back')` and `res.location('back')` to the current recommended approaches
5+
author: bjohansebas (Sebastian Beltran)
6+
license: MIT
7+
workflow: workflow.yaml
8+
category: migration
9+
10+
targets:
11+
languages:
12+
- javascript
13+
- typescript
14+
15+
keywords:
16+
- transformation
17+
- migration
18+
- express
19+
- redirect
20+
- location
21+
22+
registry:
23+
access: public
24+
visibility: public
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "@expressjs/magic-redirect",
3+
"private": true,
4+
"version": "1.0.0",
5+
"description": "Migrates usage of the legacy APIs `res.redirect('back')` and `res.location('back')`.",
6+
"type": "module",
7+
"scripts": {
8+
"test": "npx codemod jssg test -l typescript ./src/workflow.ts ./"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/expressjs/codemod.git",
13+
"directory": "recipes/magic-redirect",
14+
"bugs": "https://github.com/expressjs/codemod/issues"
15+
},
16+
"author": "bjohansebas (Sebastian Beltran)",
17+
"license": "MIT",
18+
"homepage": "https://github.com/expressjs/codemod/blob/main/recipes/magic-redirect/README.md",
19+
"devDependencies": {
20+
"@codemod.com/jssg-types": "^1.3.1"
21+
}
22+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type { SgRoot } from "@codemod.com/jssg-types/src/main";
2+
import type Js from '@codemod.com/jssg-types/src/langs/javascript';
3+
4+
async function transform(root: SgRoot<Js>): Promise<string> {
5+
const rootNode = root.root();
6+
7+
// Helper function to find the request parameter name
8+
function findRequestParamName(node: any): string {
9+
// Start from the call expression and traverse up to find function parameters
10+
let current = node;
11+
while (current) {
12+
const parent = current.parent();
13+
if (!parent) break;
14+
// Check if we're in a function declaration or arrow function
15+
if (parent.kind() === 'function_declaration' || parent.kind() === 'arrow_function') {
16+
const params = parent.field('parameters');
17+
18+
if (params && params.children().length > 0) {
19+
const firstParam = params.children()[1];
20+
if (firstParam.kind() === 'required_parameter') {
21+
const pattern = firstParam.field('pattern');
22+
if (pattern && pattern.kind() === 'identifier') {
23+
return pattern.text();
24+
}
25+
}
26+
}
27+
}
28+
29+
current = parent;
30+
}
31+
return 'req'; // default fallback
32+
}
33+
34+
// Find all redirect and location
35+
const nodes = rootNode.findAll({
36+
rule: {
37+
any: [
38+
{
39+
pattern: "$OBJ.redirect($ARG)",
40+
},
41+
{
42+
pattern: "$OBJ.location($ARG)",
43+
}]
44+
}
45+
});
46+
47+
const edits = nodes.reduce((acc: any[], node: any) => {
48+
const requestParamName = findRequestParamName(node);
49+
const obj = node.getMatch("OBJ");
50+
const arg = node.getMatch("ARG");
51+
52+
// Only transform when the argument is the literal 'back' (single or double quotes)
53+
const argText = arg && typeof arg.text === 'function' ? arg.text() : null;
54+
if (argText !== "'back'" && argText !== '"back"' && argText !== "‘back’" && argText !== "“back”") {
55+
return acc; // skip this node, no edit
56+
}
57+
58+
// Case: obj.redirect('back') or obj.location('back')
59+
const objText = obj?.text();
60+
const methodName = node.text().includes('.redirect(') ? 'redirect' : 'location';
61+
acc.push(node.replace(`${objText}.${methodName}(${requestParamName}.get("Referrer") || "/")`));
62+
return acc;
63+
}, [] as any[]);
64+
65+
const newSource = rootNode.commitEdits(edits);
66+
return newSource;
67+
}
68+
69+
export default transform;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import express from "express";
2+
import { location } from "somelibrary";
3+
4+
const app = express();
5+
6+
app.get("/", function (req, res) {
7+
res.location(req.get("Referrer") || "/");
8+
});
9+
app.get("/", (req, res) => {
10+
res.location(req.get("Referrer") || "/");
11+
});
12+
app.get("/", (req, res) => {
13+
res.location("testing");
14+
});
15+
app.get("/", (req, res) => {
16+
res.location();
17+
});
18+
app.get("/articles", function (request, response) {
19+
response.location(request.get("Referrer") || "/");
20+
});
21+
app.get("/articles", function (request, response) {
22+
response.location("testing");
23+
});
24+
app.get("/articles", (request, response) => {
25+
response.location(request.get("Referrer") || "/");
26+
});
27+
app.get("/articles", function (_req, _res) {
28+
location("back");
29+
});
30+
31+
export function handleLocation(req, res) {
32+
res.location(req.get("Referrer") || "/");
33+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import express from "express";
2+
import { redirect } from "somelibrary";
3+
4+
const app = express();
5+
6+
app.get("/", function (req, res) {
7+
res.redirect(req.get("Referrer") || "/");
8+
});
9+
app.get("/", (req, res) => {
10+
res.redirect(req.get("Referrer") || "/");
11+
});
12+
app.get("/articles", function (request, response) {
13+
response.redirect(request.get("Referrer") || "/");
14+
});
15+
app.get("/articles", (request, response) => {
16+
response.redirect(request.get("Referrer") || "/");
17+
});
18+
app.get("/articles", function (_req, _res) {
19+
redirect("back");
20+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import express from "express";
2+
import { location } from "somelibrary";
3+
4+
const app = express();
5+
6+
app.get("/", function (req, res) {
7+
res.location('back');
8+
});
9+
app.get("/", (req, res) => {
10+
res.location("back");
11+
});
12+
app.get("/", (req, res) => {
13+
res.location("testing");
14+
});
15+
app.get("/", (req, res) => {
16+
res.location();
17+
});
18+
app.get("/articles", function (request, response) {
19+
response.location("back");
20+
});
21+
app.get("/articles", function (request, response) {
22+
response.location("testing");
23+
});
24+
app.get("/articles", (request, response) => {
25+
response.location("back");
26+
});
27+
app.get("/articles", function (_req, _res) {
28+
location("back");
29+
});
30+
31+
export function handleLocation(req, res) {
32+
res.location('back');
33+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import express from "express";
2+
import { redirect } from "somelibrary";
3+
4+
const app = express();
5+
6+
app.get("/", function (req, res) {
7+
res.redirect("back");
8+
});
9+
app.get("/", (req, res) => {
10+
res.redirect("back");
11+
});
12+
app.get("/articles", function (request, response) {
13+
response.redirect("back");
14+
});
15+
app.get("/articles", (request, response) => {
16+
response.redirect("back");
17+
});
18+
app.get("/articles", function (_req, _res) {
19+
redirect("back");
20+
});

0 commit comments

Comments
 (0)