Skip to content
Draft
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 recipes/outgoingmessage-headers/codemod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
schema_version: "1.0"
name: "@nodejs/outgoingmessage-headers"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
name: "@nodejs/outgoingmessage-headers"
name: "@nodejs/http-outgoingmessage-headers"

not fan of the name but my proposition did look right @nodejs/userland-migrations any idea ?

version: 1.0.0
description: Migrate deprecated usages of OutgoingMessage.prototype._headers and ._headerNames to public HTTP header APIs
author: Node.js Team
license: MIT
workflow: workflow.yaml
category: migration

targets:
languages:
- javascript
- typescript

keywords:
- transformation
- migration
- http

registry:
access: public
visibility: public
24 changes: 24 additions & 0 deletions recipes/outgoingmessage-headers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@nodejs/outgoingmessage-headers",
"version": "1.0.0",
"description": "Migrate deprecated usages of OutgoingMessage.prototype._headers and ._headerNames to the official HTTP header APIs.",
"type": "module",
"scripts": {
"test": "npx codemod jssg test -l typescript ./src/workflow.ts ./"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nodejs/userland-migrations.git",
"directory": "recipes/outgoingmessage-headers",
"bugs": "https://github.com/nodejs/userland-migrations/issues"
},
"author": "elvessilvavieira (Elves Vieira)",
"license": "MIT",
"homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/outgoingmessage-headers/README.md",
"devDependencies": {
"@codemod.com/jssg-types": "^1.0.9"
},
"dependencies": {
"@nodejs/codemod-utils": "*"
}
}
90 changes: 90 additions & 0 deletions recipes/outgoingmessage-headers/src/workflow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import type { SgRoot, Edit } from "@codemod.com/jssg-types/main";
import type Js from "@codemod.com/jssg-types/langs/javascript";

export default function transform(root: SgRoot<Js>): string | null {
const rootNode = root.root();
const edits: Edit[] = [];
let hasChanges = false;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let hasChanges = false;


{
const matches = rootNode.findAll({
rule: { pattern: "$OBJ._headers[$KEY]" },
Copy link
Member

Choose a reason for hiding this comment

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

main part here...

Here if the user code have ah propriety expression _headers it's will be catch. We need a more complex logic to catch http usage and -> catch response objects -> catch _headers

I have to admit that the orignal issue examples wasn't 100% complete

});
for (const m of matches) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
for (const m of matches) {
for (const m of matches) {

const obj = m.getMatch("OBJ");
const key = m.getMatch("KEY");
if (!obj || !key) continue;

edits.push(m.replace(`${obj.text()}.getHeader(${key.text()})`));
hasChanges = true;
}
}

{
const matches = rootNode.findAll({
rule: { pattern: "$KEY in $OBJ._headers" },
});
for (const m of matches) {
const obj = m.getMatch("OBJ");
const key = m.getMatch("KEY");
if (!obj || !key) continue;

edits.push(m.replace(`${obj.text()}.hasHeader(${key.text()})`));
hasChanges = true;
}
}

{
const matches = rootNode.findAll({
rule: { pattern: "Object.keys($OBJ._headers)" },
});
for (const m of matches) {
const obj = m.getMatch("OBJ");
if (!obj) continue;

edits.push(m.replace(`${obj.text()}.getHeaderNames()`));
hasChanges = true;
}
}

{
const matches = rootNode.findAll({
rule: { pattern: "$OBJ._headerNames" },
});
for (const m of matches) {
const obj = m.getMatch("OBJ");
if (!obj) continue;

edits.push(m.replace(`${obj.text()}.getHeaderNames()`));
hasChanges = true;
}
}

{
const matches = rootNode.findAll({
rule: { pattern: "$OBJ._headers" },
});
for (const m of matches) {
const obj = m.getMatch("OBJ");
if (!obj) continue;

const text = m.text();
if (text.includes("[")) continue;

const parent = m.parent();
const parentText = parent?.text() ?? "";

if (parentText.startsWith("Object.keys(")) continue;

if (parentText.includes("=") && parentText.trim().startsWith(obj.text())) {
continue;
}

edits.push(m.replace(`${obj.text()}.getHeaders()`));
hasChanges = true;
}
}

if (!hasChanges) return null;
return rootNode.commitEdits(edits);
Comment on lines +88 to +89
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (!hasChanges) return null;
return rootNode.commitEdits(edits);
if (!edits.lenght) return null;
return rootNode.commitEdits(edits);

the hasChanges logic isn't needed.

}
13 changes: 13 additions & 0 deletions recipes/outgoingmessage-headers/tests/expected/basic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
function example(response, request) {
const all = response.getHeaders();
const ct = response.getHeader("content-type");
if (response.hasHeader("content-length")) console.log("has length");
console.log(response.getHeaderNames());
console.log(response.getHeaderNames());

const allReq = request.getHeaders();
const ua = request.getHeader('user-agent');
if (request.hasHeader('accept')) console.log('has accept');
console.log(request.getHeaderNames());
console.log(request.getHeaderNames());
}
13 changes: 13 additions & 0 deletions recipes/outgoingmessage-headers/tests/input/basic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
function example(response, request) {
const all = response._headers;
const ct = response._headers["content-type"];
if ("content-length" in response._headers) console.log("has length");
console.log(Object.keys(response._headers));
console.log(response._headerNames);

const allReq = request._headers;
const ua = request._headers['user-agent'];
if ('accept' in request._headers) console.log('has accept');
console.log(Object.keys(request._headers));
console.log(request._headerNames);
}
25 changes: 25 additions & 0 deletions recipes/outgoingmessage-headers/workflow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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
steps:
- name: Replace deprecated OutgoingMessage private header fields.
js-ast-grep:
js_file: src/workflow.ts
base_path: .
include:
- "**/*.js"
- "**/*.jsx"
- "**/*.mjs"
- "**/*.cjs"
- "**/*.cts"
- "**/*.mts"
- "**/*.ts"
- "**/*.tsx"
exclude:
- "**/node_modules/**"
language: typescript