From 641957c079c14c14481a325320054b1d4d43178d Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Thu, 25 Dec 2025 20:07:53 -0500 Subject: [PATCH 1/6] feat(sendfile): add migration for res.sendfile to res.sendFile --- codemods/sendfile-to-sendFile/README.md | 21 ++++++ codemods/sendfile-to-sendFile/codemod.yaml | 24 +++++++ codemods/sendfile-to-sendFile/package.json | 22 ++++++ codemods/sendfile-to-sendFile/src/workflow.ts | 33 +++++++++ .../tests/expected/send-file.ts | 67 +++++++++++++++++++ .../tests/input/send-file.ts | 67 +++++++++++++++++++ codemods/sendfile-to-sendFile/workflow.yaml | 28 ++++++++ 7 files changed, 262 insertions(+) create mode 100644 codemods/sendfile-to-sendFile/README.md create mode 100644 codemods/sendfile-to-sendFile/codemod.yaml create mode 100644 codemods/sendfile-to-sendFile/package.json create mode 100644 codemods/sendfile-to-sendFile/src/workflow.ts create mode 100644 codemods/sendfile-to-sendFile/tests/expected/send-file.ts create mode 100644 codemods/sendfile-to-sendFile/tests/input/send-file.ts create mode 100644 codemods/sendfile-to-sendFile/workflow.yaml diff --git a/codemods/sendfile-to-sendFile/README.md b/codemods/sendfile-to-sendFile/README.md new file mode 100644 index 0000000..a707f0e --- /dev/null +++ b/codemods/sendfile-to-sendFile/README.md @@ -0,0 +1,21 @@ +# Migrate legacy `res.sendfile(file)` to `res.sendFile(file)` + +Migrates usage of the legacy APIs `res.sendfile(file)` to `res.sendFile(file)`. + +## Example + +### Migrating `res.sendfile(file)` + +The migration involves replacing instances of `res.sendfile(file)` with `res.sendFile(file)`. + +```diff +app.get('/some-route', (req, res) => { + // Some logic here +- res.sendfile('/path/to/file'); ++ res.sendFile('/path/to/file'); +}); +``` + +## References + +- [Migration of res.sendfile(file)](https://expressjs.com/en/guide/migrating-5.html#res.sendFile) \ No newline at end of file diff --git a/codemods/sendfile-to-sendFile/codemod.yaml b/codemods/sendfile-to-sendFile/codemod.yaml new file mode 100644 index 0000000..e0b8184 --- /dev/null +++ b/codemods/sendfile-to-sendFile/codemod.yaml @@ -0,0 +1,24 @@ +schema_version: "1.0" +name: "@expressjs/sendfile-to-sendFile" +version: "1.0.0" +description: Migrates usage of the legacy API `res.sendfile(file)` to `res.sendFile(file)` +author: bjohansebas (Sebastian Beltran) +license: MIT +workflow: workflow.yaml +category: migration + +targets: + languages: + - javascript + - typescript + +keywords: + - transformation + - migration + - express + - sendFile + - files + +registry: + access: public + visibility: public \ No newline at end of file diff --git a/codemods/sendfile-to-sendFile/package.json b/codemods/sendfile-to-sendFile/package.json new file mode 100644 index 0000000..9bdc47c --- /dev/null +++ b/codemods/sendfile-to-sendFile/package.json @@ -0,0 +1,22 @@ +{ + "name": "@expressjs/sendfile-to-sendfile", + "private": true, + "version": "1.0.0", + "description": "Migrates usage of the legacy API `res.sendfile(file)` to `res.sendFile(file)`.", + "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/back-redirect-deprecated", + "bugs": "https://github.com/expressjs/codemod/issues" + }, + "author": "bjohansebas (Sebastian Beltran)", + "license": "MIT", + "homepage": "https://github.com/expressjs/codemod/blob/main/codemods/back-redirect-deprecated/README.md", + "devDependencies": { + "@codemod.com/jssg-types": "^1.3.1" + } +} diff --git a/codemods/sendfile-to-sendFile/src/workflow.ts b/codemods/sendfile-to-sendFile/src/workflow.ts new file mode 100644 index 0000000..57b2667 --- /dev/null +++ b/codemods/sendfile-to-sendFile/src/workflow.ts @@ -0,0 +1,33 @@ +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): Promise { + const rootNode = root.root() + + const nodes = rootNode.findAll({ + rule: { + pattern: '$OBJ.$METHOD($$$METHOD)', + }, + constraints: { + METHOD: { regex: '^(sendfile)$' }, + }, + }) + + const edits: Edit[] = [] + + for (const call of nodes) { + const method = call.getMatch('METHOD') + const obj = call.getMatch('OBJ') + if (!method || !obj) continue + + const objDef = obj.definition({ resolveExternal: false }) + if (!objDef) continue + + edits.push(method.replace('sendFile')) + } + + if (edits.length === 0) return null + return rootNode.commitEdits(edits) +} + +export default transform diff --git a/codemods/sendfile-to-sendFile/tests/expected/send-file.ts b/codemods/sendfile-to-sendFile/tests/expected/send-file.ts new file mode 100644 index 0000000..f222bc4 --- /dev/null +++ b/codemods/sendfile-to-sendFile/tests/expected/send-file.ts @@ -0,0 +1,67 @@ +import express from "express"; +import path from "path"; +import sendfile from "other-place" + +const app = express(); +const options = { + root: path.join(__dirname, 'public'), + dotfiles: 'deny', + headers: { + 'x-timestamp': Date.now(), + 'x-sent': true + } +} + +const sharedSendFile = (res, next, fileName) => { + res.sendFile(fileName, options, (err) => { + if (err) { + next(err) + } else { + console.log('Sent:', fileName) + } + }) +} + +app.get('/file/:name', (req, res, next) => { + res.sendFile() +}) + +app.get('/file/:name', (req, res, next) => { + res.sendFile("file.txt") +}) + +app.get('/file/:name', (req, res, next) => { + const fileName = req.params.name + + res.sendFile(fileName, options, (err) => { + if (err) { + next(err) + } else { + console.log('Sent:', fileName) + } + }) + sharedSendFile(res, next, fileName); +}) + +app.get('/filename/:name', function (req, res, next) { + const fileName = req.params.name + + res.sendFile(fileName, options, (err) => { + if (err) { + next(err) + } else { + console.log('Sent:', fileName) + } + }) + sharedSendFile(res, next, fileName); +}) + +app.get('/file-handler', (req, res, next) => { + sendfile('test', options, (err) => { + if (err) { + next(err) + } else { + console.log('Sent:', 'test') + } + }) +}) \ No newline at end of file diff --git a/codemods/sendfile-to-sendFile/tests/input/send-file.ts b/codemods/sendfile-to-sendFile/tests/input/send-file.ts new file mode 100644 index 0000000..37926ea --- /dev/null +++ b/codemods/sendfile-to-sendFile/tests/input/send-file.ts @@ -0,0 +1,67 @@ +import express from "express"; +import path from "path"; +import sendfile from "other-place" + +const app = express(); +const options = { + root: path.join(__dirname, 'public'), + dotfiles: 'deny', + headers: { + 'x-timestamp': Date.now(), + 'x-sent': true + } +} + +const sharedSendFile = (res, next, fileName) => { + res.sendfile(fileName, options, (err) => { + if (err) { + next(err) + } else { + console.log('Sent:', fileName) + } + }) +} + +app.get('/file/:name', (req, res, next) => { + res.sendfile() +}) + +app.get('/file/:name', (req, res, next) => { + res.sendfile("file.txt") +}) + +app.get('/file/:name', (req, res, next) => { + const fileName = req.params.name + + res.sendfile(fileName, options, (err) => { + if (err) { + next(err) + } else { + console.log('Sent:', fileName) + } + }) + sharedSendFile(res, next, fileName); +}) + +app.get('/filename/:name', function (req, res, next) { + const fileName = req.params.name + + res.sendfile(fileName, options, (err) => { + if (err) { + next(err) + } else { + console.log('Sent:', fileName) + } + }) + sharedSendFile(res, next, fileName); +}) + +app.get('/file-handler', (req, res, next) => { + sendfile('test', options, (err) => { + if (err) { + next(err) + } else { + console.log('Sent:', 'test') + } + }) +}) \ No newline at end of file diff --git a/codemods/sendfile-to-sendFile/workflow.yaml b/codemods/sendfile-to-sendFile/workflow.yaml new file mode 100644 index 0000000..0749364 --- /dev/null +++ b/codemods/sendfile-to-sendFile/workflow.yaml @@ -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.sendfile(file)` to `res.sendFile(file)` + 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 \ No newline at end of file From 98f50686388c5678c3224bba7e6bc45b28a1b383 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Thu, 25 Dec 2025 20:10:56 -0500 Subject: [PATCH 2/6] fix(package): update repository directory and homepage in package.json --- codemods/sendfile-to-sendFile/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codemods/sendfile-to-sendFile/package.json b/codemods/sendfile-to-sendFile/package.json index 9bdc47c..68d6d16 100644 --- a/codemods/sendfile-to-sendFile/package.json +++ b/codemods/sendfile-to-sendFile/package.json @@ -10,12 +10,12 @@ "repository": { "type": "git", "url": "git+https://github.com/expressjs/codemod.git", - "directory": "codemods/back-redirect-deprecated", + "directory": "codemods/sendfile-to-sendFile", "bugs": "https://github.com/expressjs/codemod/issues" }, "author": "bjohansebas (Sebastian Beltran)", "license": "MIT", - "homepage": "https://github.com/expressjs/codemod/blob/main/codemods/back-redirect-deprecated/README.md", + "homepage": "https://github.com/expressjs/codemod/blob/main/codemods/sendfile-to-sendFile/README.md", "devDependencies": { "@codemod.com/jssg-types": "^1.3.1" } From 52402f7ad3d9668b47f425fcaae5644ea2ae4153 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Thu, 25 Dec 2025 20:13:24 -0500 Subject: [PATCH 3/6] fix: update package-lock --- package-lock.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/package-lock.json b/package-lock.json index aeac2ae..f65e18b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,6 +56,14 @@ "@codemod.com/jssg-types": "^1.3.1" } }, + "codemods/sendfile-to-sendFile": { + "name": "@expressjs/sendfile-to-sendfile", + "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", @@ -1017,6 +1025,10 @@ "resolved": "codemods/back-redirect-deprecated", "link": true }, + "node_modules/@expressjs/sendfile-to-sendfile": { + "resolved": "codemods/sendfile-to-sendFile", + "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", From a118500927395bfbc6ee80ea7053ec3a8ae98af8 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Thu, 25 Dec 2025 22:42:39 -0500 Subject: [PATCH 4/6] rename codemod --- .../{sendfile-to-sendFile => camelcase-sendfile}/README.md | 0 .../codemod.yaml | 2 +- .../package.json | 6 +++--- .../src/workflow.ts | 0 .../tests/expected/send-file.ts | 0 .../tests/input/send-file.ts | 0 .../workflow.yaml | 0 7 files changed, 4 insertions(+), 4 deletions(-) rename codemods/{sendfile-to-sendFile => camelcase-sendfile}/README.md (100%) rename codemods/{sendfile-to-sendFile => camelcase-sendfile}/codemod.yaml (91%) rename codemods/{sendfile-to-sendFile => camelcase-sendfile}/package.json (81%) rename codemods/{sendfile-to-sendFile => camelcase-sendfile}/src/workflow.ts (100%) rename codemods/{sendfile-to-sendFile => camelcase-sendfile}/tests/expected/send-file.ts (100%) rename codemods/{sendfile-to-sendFile => camelcase-sendfile}/tests/input/send-file.ts (100%) rename codemods/{sendfile-to-sendFile => camelcase-sendfile}/workflow.yaml (100%) diff --git a/codemods/sendfile-to-sendFile/README.md b/codemods/camelcase-sendfile/README.md similarity index 100% rename from codemods/sendfile-to-sendFile/README.md rename to codemods/camelcase-sendfile/README.md diff --git a/codemods/sendfile-to-sendFile/codemod.yaml b/codemods/camelcase-sendfile/codemod.yaml similarity index 91% rename from codemods/sendfile-to-sendFile/codemod.yaml rename to codemods/camelcase-sendfile/codemod.yaml index e0b8184..f8c8b9b 100644 --- a/codemods/sendfile-to-sendFile/codemod.yaml +++ b/codemods/camelcase-sendfile/codemod.yaml @@ -1,5 +1,5 @@ schema_version: "1.0" -name: "@expressjs/sendfile-to-sendFile" +name: "@expressjs/camelcase-sendfile" version: "1.0.0" description: Migrates usage of the legacy API `res.sendfile(file)` to `res.sendFile(file)` author: bjohansebas (Sebastian Beltran) diff --git a/codemods/sendfile-to-sendFile/package.json b/codemods/camelcase-sendfile/package.json similarity index 81% rename from codemods/sendfile-to-sendFile/package.json rename to codemods/camelcase-sendfile/package.json index 68d6d16..dc5275f 100644 --- a/codemods/sendfile-to-sendFile/package.json +++ b/codemods/camelcase-sendfile/package.json @@ -1,5 +1,5 @@ { - "name": "@expressjs/sendfile-to-sendfile", + "name": "@expressjs/camelcase-sendfile", "private": true, "version": "1.0.0", "description": "Migrates usage of the legacy API `res.sendfile(file)` to `res.sendFile(file)`.", @@ -10,12 +10,12 @@ "repository": { "type": "git", "url": "git+https://github.com/expressjs/codemod.git", - "directory": "codemods/sendfile-to-sendFile", + "directory": "codemods/camelcase-sendfile", "bugs": "https://github.com/expressjs/codemod/issues" }, "author": "bjohansebas (Sebastian Beltran)", "license": "MIT", - "homepage": "https://github.com/expressjs/codemod/blob/main/codemods/sendfile-to-sendFile/README.md", + "homepage": "https://github.com/expressjs/codemod/blob/main/codemods/camelcase-sendfile/README.md", "devDependencies": { "@codemod.com/jssg-types": "^1.3.1" } diff --git a/codemods/sendfile-to-sendFile/src/workflow.ts b/codemods/camelcase-sendfile/src/workflow.ts similarity index 100% rename from codemods/sendfile-to-sendFile/src/workflow.ts rename to codemods/camelcase-sendfile/src/workflow.ts diff --git a/codemods/sendfile-to-sendFile/tests/expected/send-file.ts b/codemods/camelcase-sendfile/tests/expected/send-file.ts similarity index 100% rename from codemods/sendfile-to-sendFile/tests/expected/send-file.ts rename to codemods/camelcase-sendfile/tests/expected/send-file.ts diff --git a/codemods/sendfile-to-sendFile/tests/input/send-file.ts b/codemods/camelcase-sendfile/tests/input/send-file.ts similarity index 100% rename from codemods/sendfile-to-sendFile/tests/input/send-file.ts rename to codemods/camelcase-sendfile/tests/input/send-file.ts diff --git a/codemods/sendfile-to-sendFile/workflow.yaml b/codemods/camelcase-sendfile/workflow.yaml similarity index 100% rename from codemods/sendfile-to-sendFile/workflow.yaml rename to codemods/camelcase-sendfile/workflow.yaml From 1e6f1618736187a1df27712653ecf9bda2029543 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Wed, 7 Jan 2026 10:22:43 -0500 Subject: [PATCH 5/6] Update codemods/camelcase-sendfile/codemod.yaml Signed-off-by: Sebastian Beltran --- codemods/camelcase-sendfile/codemod.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/codemods/camelcase-sendfile/codemod.yaml b/codemods/camelcase-sendfile/codemod.yaml index f8c8b9b..1f2a6ac 100644 --- a/codemods/camelcase-sendfile/codemod.yaml +++ b/codemods/camelcase-sendfile/codemod.yaml @@ -5,6 +5,7 @@ description: Migrates usage of the legacy API `res.sendfile(file)` to `res.sendF author: bjohansebas (Sebastian Beltran) license: MIT workflow: workflow.yaml +repository: "https://github.com/expressjs/codemod/tree/HEAD/codemods/camelcase-sendfile" category: migration targets: From 7d938846da1cfad9e424bccc7cf62ab9722443f2 Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Fri, 9 Jan 2026 17:28:47 -0500 Subject: [PATCH 6/6] fix(workflow): simplify empty checks for nodes and edits --- codemods/camelcase-sendfile/src/workflow.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/codemods/camelcase-sendfile/src/workflow.ts b/codemods/camelcase-sendfile/src/workflow.ts index 57b2667..1238746 100644 --- a/codemods/camelcase-sendfile/src/workflow.ts +++ b/codemods/camelcase-sendfile/src/workflow.ts @@ -13,6 +13,8 @@ async function transform(root: SgRoot): Promise { }, }) + if (!nodes.length) return null + const edits: Edit[] = [] for (const call of nodes) { @@ -26,7 +28,7 @@ async function transform(root: SgRoot): Promise { edits.push(method.replace('sendFile')) } - if (edits.length === 0) return null + if (!edits.length) return null return rootNode.commitEdits(edits) }