Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions codemods/redirect-arg-order/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Migrate legacy `res.redirect(url, status)`

Migrates usage of the legacy APIs `res.redirect(url, status)` to the new signature

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to mention the affected version

`res.redirect(status, url)`.

## Example

### Migrating `res.redirect(url, status)`

The migration involves replacing instances of `res.redirect(url, status)` with `res.redirect(status, url)`.

```diff
app.get('/some-route', (req, res) => {
// Some logic here
- res.redirect(url, status);
+ res.redirect(status, url);
});
```

## References

- [Migration of res.redirect(url, status)](https://expressjs.com/en/guide/migrating-5.html#res.redirect)
25 changes: 25 additions & 0 deletions codemods/redirect-arg-order/codemod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
schema_version: "1.0"
name: "@expressjs/redirect-arg-order"
version: "1.0.0"
description: Migrates usage of the legacy APIs `res.redirect(url, status)` to use the recommended argument order `res.redirect(status, url)`.
author: bjohansebas (Sebastian Beltran)
license: MIT
workflow: workflow.yaml
repository: "https://github.com/expressjs/codemod/tree/HEAD/codemods/redirect-arg-order"
category: migration

targets:
languages:
- javascript
- typescript

keywords:
- transformation
- migration
- express
- redirect
- location

registry:
access: public
visibility: public
22 changes: 22 additions & 0 deletions codemods/redirect-arg-order/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@expressjs/redirect-arg-order",
"private": true,
"version": "1.0.0",
"description": "Migrates usage of the legacy APIs `res.redirect(url, status)` to use the recommended argument order `res.redirect(status, url)`.",
"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": "codemods/redirect-arg-order",
"bugs": "https://github.com/expressjs/codemod/issues"
},
"author": "bjohansebas (Sebastian Beltran)",
"license": "MIT",
"homepage": "https://github.com/expressjs/codemod/blob/main/codemods/redirect-arg-order/README.md",
"devDependencies": {
"@codemod.com/jssg-types": "^1.3.1"
}
}
44 changes: 44 additions & 0 deletions codemods/redirect-arg-order/src/workflow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type Js from '@codemod.com/jssg-types/src/langs/javascript'
import type { Edit, SgRoot } from '@codemod.com/jssg-types/src/main'

async function transform(root: SgRoot<Js>): Promise<string | null> {
const rootNode = root.root()

const nodes = rootNode.findAll({
rule: {
pattern: '$OBJ.$METHOD($$$ARG)',
},
constraints: {
METHOD: { regex: '^(redirect)$' },
},
})

if (!nodes.length) return null

const edits: Edit[] = []

for (const call of nodes) {
const obj = call.getMatch('OBJ')
const args = call.getMultipleMatches('ARG')
if (!obj || args.length < 3) continue

const objDef = obj.definition({ resolveExternal: false })
if (!objDef) continue

// $$$ARG yields argument nodes interleaved with separators, so arg nodes are at 0,2,4...
const first = args[0]
const second = args[2]
if (!first || !second) continue

// Only transform legacy form redirect(url, status) where second is number
if (second.is('number') && !first.is('number')) {
edits.push(call.replace(`${obj.text()}.redirect(${second.text()}, ${first.text()})`))
}
}

if (!edits.length) return null

return rootNode.commitEdits(edits)
}

export default transform
38 changes: 38 additions & 0 deletions codemods/redirect-arg-order/tests/expected/redirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import express from "express";
import { redirect } from "somelibrary";

const app = express();

app.get("/", function (...arg) {
const [, res] = arg
res.redirect();
});

app.get("/", function (...arg) {
const [, res] = arg
res.redirect(301, "/other-page");
});

app.get("/", function (req, res) {
res.redirect();
});

app.get("/", function (req, res) {
res.redirect(301, "/other-page");
});

app.get("/", function (req, response) {
response.redirect(301, "/other-page");
});

app.get("/", function (req, res) {
res.redirect(301, "/other-page");
});

app.get("/", function (req, res) {
res.redirect("/other-page");
});

app.get("/", function (req, res) {
redirect(301, "/other-page");
});
38 changes: 38 additions & 0 deletions codemods/redirect-arg-order/tests/input/redirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import express from "express";
import { redirect } from "somelibrary";

const app = express();

app.get("/", function (...arg) {
const [, res] = arg
res.redirect();
});

app.get("/", function (...arg) {
const [, res] = arg
res.redirect("/other-page", 301);
});

app.get("/", function (req, res) {
res.redirect();
});

app.get("/", function (req, res) {
res.redirect("/other-page", 301);
});

app.get("/", function (req, response) {
response.redirect("/other-page", 301);
});

app.get("/", function (req, res) {
res.redirect(301, "/other-page");
});

app.get("/", function (req, res) {
res.redirect("/other-page");
});

app.get("/", function (req, res) {
redirect(301, "/other-page");
});
28 changes: 28 additions & 0 deletions codemods/redirect-arg-order/workflow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# 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(url, status)` to use the recommended argument order `res.redirect(status, url)`.
js-ast-grep:
js_file: src/workflow.ts
base_path: .
semantic_analysis: file
include:
- "**/*.cjs"
- "**/*.js"
- "**/*.jsx"
- "**/*.mjs"
- "**/*.cts"
- "**/*.mts"
- "**/*.ts"
- "**/*.tsx"
exclude:
- "**/node_modules/**"
language: typescript
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.