Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6ef1d20
working version with test and README for TLSSocket
Nov 27, 2025
1c4fe70
Merge remote-tracking branch 'origin/main' into MaximeD
Nov 27, 2025
c638006
recipes(tls): add tls-securepair-to-tlssocket codemod
Dec 5, 2025
485fb15
Merge branch 'main' into MaximeD
menace31 Dec 5, 2025
3e43dea
recipes(tls): add tls-securepair-to-tlssocket codemod
Dec 5, 2025
3e78f7b
chore: update package-lock.json
Dec 5, 2025
2a069ed
Update recipes/tls-securepair-to-tlssocket/README.md
menace31 Dec 10, 2025
52de208
Update recipes/tls-securepair-to-tlssocket/README.md
menace31 Dec 10, 2025
b12b649
Update recipes/tls-securepair-to-tlssocket/src/workflow.ts
menace31 Dec 10, 2025
fe681b9
Update recipes/tls-securepair-to-tlssocket/src/workflow.ts
menace31 Dec 10, 2025
153faf9
use nodejs mod-utils and remove test.sh
Dec 10, 2025
40986bd
use nodejs mod-utils and remove test.sh
Dec 10, 2025
0cd5eee
use nodejs mod-utils and remove test.sh
Dec 10, 2025
af0f097
codemod-update
Dec 11, 2025
a2b7796
Update recipes/tls-securepair-to-tlssocket/src/workflow.ts
menace31 Dec 12, 2025
6f891fe
Update recipes/tls-securepair-to-tlssocket/src/workflow.ts
menace31 Dec 12, 2025
9d501a2
correct new_line in test files
Dec 12, 2025
8fbb180
remove disfunctional test
menace31 Dec 16, 2025
9cdb7cc
fix windows tests
menace31 Dec 16, 2025
91216ec
Merge branch 'MaximeD' of https://github.com/menace31/userland-migrat…
menace31 Dec 16, 2025
4148701
restore tests
menace31 Dec 16, 2025
700224b
fix tests
menace31 Dec 16, 2025
28a3357
fix tests
menace31 Dec 16, 2025
9bcc4d9
Merge branch 'main' into MaximeD
menace31 Dec 16, 2025
b6a95fa
fix tests
menace31 Dec 16, 2025
62a72f7
remove IA comment
menace31 Dec 16, 2025
0a1f785
Merge branch 'MaximeD' of https://github.com/menace31/userland-migrat…
menace31 Dec 16, 2025
8ffc05c
update package-lock.json
menace31 Dec 16, 2025
b18b065
Merge branch 'main' into MaximeD
menace31 Dec 17, 2025
9c54544
Update recipes/tls-securepair-to-tlssocket/src/workflow.ts
menace31 Dec 18, 2025
d22e788
pull
menace31 Dec 18, 2025
be3ccee
Update recipes/tls-securepair-to-tlssocket/src/workflow.ts
menace31 Dec 18, 2025
43324bf
fix test
menace31 Dec 18, 2025
9b0dd42
fix test
menace31 Dec 18, 2025
df79099
fix merge
menace31 Dec 18, 2025
2560ceb
fix test
menace31 Dec 18, 2025
8fd0b6c
doc update
menace31 Dec 23, 2025
9d3224c
fix comments
menace31 Dec 23, 2025
23e8baa
Merge branch 'main' into MaximeD
menace31 Dec 27, 2025
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
98 changes: 98 additions & 0 deletions recipes/tls-securepair-to-tlssocket/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# tls-securepair-to-tlssocket

Codemod to migrate from the deprecated `tls.SecurePair` class to `tls.TLSSocket` in Node.js applications. `SecurePair` was deprecated and subsequently removed in favor of `TLSSocket`.

## What it does

This codemod transforms usages of `tls.SecurePair` into `tls.TLSSocket`. Since `TLSSocket` wraps an existing socket, the codemod injects a `socket` argument that you may need to define or bind in your context.

Key transformations:
- **Constructor:** Replaces `new SecurePair()` with `new TLSSocket(socket)`.
- **Imports:** Updates `require` and `import` statements from `SecurePair` to `TLSSocket`.
- **Renaming:** Intelligently renames variables (e.g., `pair` → `socket`, `securePairInstance` → `socketInstance`) while preserving CamelCase.
- **Cleanup:** Removes deprecated property accesses like `.cleartext` and `.encrypted`.
- **Annotations:** Adds comments to highlight where manual API verification is needed.

## Supports

- **Module Systems:**
- CommonJS: `const tls = require('node:tls')` / `const { SecurePair } = ...`
- ESM: `import tls from 'node:tls'` / `import { SecurePair } ...`
- **Variable Renaming:**
- Updates variable declarations: `const pair = ...` → `const socket = ...`
- Updates references deep in the scope: `pair.on('error')` → `socket.on('error')`
- Handles naming variations: `myPair` → `mySocket`, `securePair` → `secureSocket`.
- **Cleanup:**
- Identifies and removes lines accessing `cleartext` or `encrypted` properties.
- **Namespace Handling:**
- Supports both `new tls.SecurePair()` and `new SecurePair()`.

## Examples

### Case 1: CommonJS & Variable Renaming

**Before**

```js
const tls = require('node:tls');

// Using tls.SecurePair constructor
const pair = new tls.SecurePair();
const cleartext = pair.cleartext;
const encrypted = pair.encrypted;

pair.on('error', (err) => {
console.error(err);
});
```

**After**

```
const tls = require('node:tls');

// Using tls.TLSSocket instead
const socket = new tls.TLSSocket(socket);
// Note: Direct migration may require additional context-specific changes
// as SecurePair and TLSSocket have different APIs

socket.on('error', (err) => {
console.error(err);
});
```

### Case 2: ESM & Destructuring

**Before**

```
import { SecurePair } from 'node:tls';

const myPair = new SecurePair();
myPair.cleartext.write('hello');
```

**After**

```
import { TLSSocket } from 'node:tls';

const mySocket = new TLSSocket(socket);
// Note: Direct migration may require additional context-specific changes
// as SecurePair and TLSSocket have different APIs
```

## Warning

The tls.TLSSocket constructor requires an existing socket instance (net.Socket) as an argument. This codemod automatically inserts socket as the argument:
JavaScript

```
new TLSSocket(socket)
```

You must ensure that a variable named socket exists in the scope or rename it to match your existing socket variable (e.g., clientSocket, stream, etc.).

## Test

The test.sh script runs all the tests located in the tests folder. All input files are temporarily copied to a new folder and compared against their expected results found in the expected folder. This helps identify which tests failed and why. Feel free to add new tests if necessary.
23 changes: 23 additions & 0 deletions recipes/tls-securepair-to-tlssocket/codemod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
schema_version: "1.0"
name: "@nodejs/tls-securepair-to-tlssocket"
version: "1.0.0"
description: Migrate usages of `tls.SecurePair` to `tls.TLSSocket` where possible.
author: Maxime Devillet
license: MIT
workflow: workflow.yaml
category: migration

targets:
languages:
- javascript
- typescript

keywords:
- transformation
- migration
- tls
- securepair

registry:
access: public
visibility: public
25 changes: 25 additions & 0 deletions recipes/tls-securepair-to-tlssocket/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@nodejs/tls-securepair-to-tlssocket",
"version": "1.0.0",
"description": "Migrate usages of tls.SecurePair to tls.TLSSocket.",
"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/tls-securepair-to-tlssocket",
"bugs": "https://github.com/nodejs/userland-migrations/issues"
},
"author": "Maxime Devillet",
"license": "MIT",
"homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/tls-securepair-to-tlssocket/README.md",
"devDependencies": {
"@codemod.com/jssg-types": "^1.0.9"
},
"dependencies": {
"@nodejs/codemod-utils": "*",
"@ast-grep/napi": "^0.40.0"
}
}
132 changes: 132 additions & 0 deletions recipes/tls-securepair-to-tlssocket/src/workflow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { removeLines } from "@nodejs/codemod-utils/ast-grep/remove-lines";
import type { Edit, SgRoot, Range } from "@codemod.com/jssg-types/main";
import type Js from "@codemod.com/jssg-types/langs/javascript";

function getClosest(node: any, kinds: string[]): any | null {
let current = node.parent();
while (current) {
if (kinds.includes(current.kind())) {
return current;
}
current = current.parent();
}
return null;
}

export default function transform(root: SgRoot<Js>): string | null {
const rootNode = root.root();
const edits: Edit[] = [];
const linesToRemove: Range[] = [];

const importNodes = rootNode.findAll({
Copy link
Member

Choose a reason for hiding this comment

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

rule: {
any: [
{ kind: "import_specifier", has: { kind: "identifier", regex: "^SecurePair$" } },
{ kind: "shorthand_property_identifier_pattern", regex: "^SecurePair$" },
{ kind: "property_identifier", regex: "^SecurePair$" }
]
}
});

for (const node of importNodes) {
if (node.text() === "SecurePair") {
edits.push(node.replace("TLSSocket"));
}
}

const newExpressions = rootNode.findAll({
rule: {
kind: "new_expression",
has: {
any: [
{ kind: "member_expression", has: { field: "property", regex: "^SecurePair$" } },
{ kind: "identifier", regex: "^SecurePair$" }
]
}
}
});

for (const node of newExpressions) {
const callee = node.field("constructor");
if (!callee) continue;

let newConstructorName = "TLSSocket";
if (callee.kind() === "member_expression") {
const object = callee.field("object");
if (object) {
newConstructorName = `${object.text()}.TLSSocket`;
}
}

edits.push(node.replace(`new ${newConstructorName}(socket)`));

const declarator = getClosest(node, ["variable_declarator"]);
if (declarator) {
const idNode = declarator.field("name");
if (idNode) {
const oldName = idNode.text();

let newName = "socket";
if (oldName !== "pair" && oldName !== "SecurePair") {
if (oldName.includes("Pair")) {
newName = oldName.replace("Pair", "Socket");
}
else if (oldName.includes("pair")) {
newName = oldName.replace("pair", "socket");
}
else {
newName = "socket";
}
}

const obsoleteUsages = rootNode.findAll({
rule: {
kind: "member_expression",
all: [
{ has: { field: "object", regex: `^${oldName}$` } },
{ has: { field: "property", regex: "^(cleartext|encrypted)$" } }
]
}
});

for (const usage of obsoleteUsages) {
const statement = getClosest(usage, ["lexical_declaration", "expression_statement"]);
if (statement) {
linesToRemove.push(statement.range());
}
}

edits.push(idNode.replace(newName));

const references = rootNode.findAll({
rule: {
kind: "identifier",
regex: `^${oldName}$`
}
});

for (const ref of references) {
const parent = ref.parent();
if (parent && parent.kind() === 'member_expression') {
const property = parent.field('property');
if (property && property.id() === ref.id()) {
continue;
}
}

if (parent && (parent.kind() === 'import_specifier' || parent.kind() === 'shorthand_property_identifier_pattern')) {
continue;
}

if (ref.id() === idNode.id()) continue;

edits.push(ref.replace(newName));
}
}
}
}

let sourceCode = rootNode.commitEdits(edits);
sourceCode = removeLines(sourceCode, linesToRemove);
return sourceCode;
}
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
}
}

74 changes: 74 additions & 0 deletions recipes/tls-securepair-to-tlssocket/test.sh
Copy link
Member

Choose a reason for hiding this comment

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

this file isn't needed codemod cli furnish us the needed tooling

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/bin/bash

GREEN='\033[0;32m'
RED='\033[0;31m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
BOLD='\033[1m'
NC='\033[0m'

INPUT_DIR="tests/input"
EXPECTED_DIR="tests/expected"
TEMP_DIR="tests/temp_workzone"

declare -a passed_files
declare -a failed_files

echo -e "${BLUE}${BOLD}=== Starting Codemod Test Suite ===${NC}"

rm -rf "$TEMP_DIR"
mkdir -p "$TEMP_DIR"
cp -r "$INPUT_DIR"/* "$TEMP_DIR"

echo -e "➜ Running codemod on temporary files..."
npx codemod workflow run -w workflow.yaml -t "$TEMP_DIR" > /dev/null 2>&1

echo -e "➜ Verifying results...\n"

for expected_file in "$EXPECTED_DIR"/*; do
filename=$(basename "$expected_file")
generated_file="$TEMP_DIR/$filename"

if [ ! -f "$generated_file" ]; then
echo -e "${RED} [MISSING] $filename${NC}"
failed_files+=("$filename (Missing)")
continue
fi

diff_output=$(diff -u --color=always "$expected_file" "$generated_file")
exit_code=$?

if [ $exit_code -eq 0 ]; then
echo -e " ${GREEN}✔ $filename${NC}"
passed_files+=("$filename")
else
echo -e " ${RED}✘ $filename${NC}"
echo -e "${YELLOW}--- Differences for $filename ---${NC}"
echo "$diff_output"
echo -e "${YELLOW}----------------------------------${NC}\n"
failed_files+=("$filename")
fi
done

rm -rf "$TEMP_DIR"

echo -e "\n${BLUE}${BOLD}=== FINAL REPORT ===${NC}"

if [ ${#passed_files[@]} -gt 0 ]; then
echo -e "\n${GREEN}${BOLD}Passed Tests (${#passed_files[@]}) :${NC}"
for f in "${passed_files[@]}"; do
echo -e " ${GREEN}✔ $f${NC}"
done
fi

if [ ${#failed_files[@]} -gt 0 ]; then
echo -e "\n${RED}${BOLD}Failed Tests (${#failed_files[@]}) :${NC}"
for f in "${failed_files[@]}"; do
echo -e " ${RED}✘ $f${NC}"
done
echo -e "\n${RED}➔ Result: FAILURE${NC}"
exit 1
else
echo -e "\n${GREEN}➔ Result: SUCCESS${NC}"
exit 0
fi
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const tls = require('node:tls');
const fs = require('fs'); // Code non lié

function createSecureConnection() {
// Using tls.SecurePair constructor
const socket = new tls.TLSSocket(socket);

// Ces lignes doivent disparaître

return socket;
}
8 changes: 8 additions & 0 deletions recipes/tls-securepair-to-tlssocket/tests/expected/file-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const tls = require('node:tls');

// Using tls.SecurePair constructor
const socket = new tls.TLSSocket(socket);

// Direct import
const { TLSSocket } = require('node:tls');
const socket2 = new TLSSocket(socket);
8 changes: 8 additions & 0 deletions recipes/tls-securepair-to-tlssocket/tests/expected/file-2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import tls from 'node:tls';

// Using tls.SecurePair constructor
const socket = new tls.TLSSocket(socket);

// Direct import
import { TLSSocket } from 'node:tls';
const socket2 = new TLSSocket(socket);
Loading
Loading