diff --git a/package-lock.json b/package-lock.json index b778d696..9518b7c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1556,6 +1556,10 @@ "resolved": "recipes/slow-buffer-to-buffer-alloc-unsafe-slow", "link": true }, + "node_modules/@nodejs/tls-create-secure-pair-to-tls-socket": { + "resolved": "recipes/tls-create-secure-pair-to-tls-socket", + "link": true + }, "node_modules/@nodejs/tmpdir-to-tmpdir": { "resolved": "recipes/tmpdir-to-tmpdir", "link": true @@ -4500,6 +4504,17 @@ "@codemod.com/jssg-types": "^1.3.1" } }, + "recipes/tls-create-secure-pair-to-tls-socket": { + "name": "@nodejs/tls-create-secure-pair-to-tls-socket", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@nodejs/codemod-utils": "*" + }, + "devDependencies": { + "@codemod.com/jssg-types": "^1.0.9" + } + }, "recipes/tmpdir-to-tmpdir": { "name": "@nodejs/tmpdir-to-tmpdir", "version": "1.0.1", diff --git a/recipes/tls-create-secure-pair-to-tls-socket/README.md b/recipes/tls-create-secure-pair-to-tls-socket/README.md new file mode 100644 index 00000000..d66f541a --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/README.md @@ -0,0 +1,104 @@ +# `tls.createSecurePair` deprecation DEP0064 + +This recipe transforms the usage from the deprecated `createSecurePair()` to `TLSSocket()`. + +See [DEP0064](https://nodejs.org/api/deprecations.html#dep0064-tlscreatesecurepair). + +## Examples + +### 1) Basic `createSecurePair` usage +```diff +-const { createSecurePair } = require('node:tls'); +-const pair = createSecurePair(credentials); ++const { TLSSocket } = require('node:tls'); ++const socket = new TLSSocket(underlyingSocket, { secureContext: credentials }); +``` + +--- + +### 2) Namespace import (CJS) +```diff +-const tls = require('node:tls'); +-const pair = tls.createSecurePair(credentials); ++const tls = require('node:tls'); ++const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); +``` + +--- + +### 3) With server context +```diff +-const { createSecurePair } = require('node:tls'); +-const pair = createSecurePair(credentials, true, true, false); ++const { TLSSocket } = require('node:tls'); ++const socket = new TLSSocket(underlyingSocket, { ++ secureContext: credentials, ++ isServer: true, ++ requestCert: true, ++ rejectUnauthorized: false ++}); +``` + +--- + +### 4) ESM named import +```diff +-import { createSecurePair } from 'node:tls'; +-const pair = createSecurePair(credentials); ++import { TLSSocket } from 'node:tls'; ++const socket = new TLSSocket(underlyingSocket, { secureContext: credentials }); +``` + +--- + +### 5) ESM namespace import +```diff +-import * as tls from 'node:tls'; +-const pair = tls.createSecurePair(credentials); ++import * as tls from 'node:tls'; ++const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); +``` + +--- + +### 6) Mixed usage with other TLS functions +```diff +-const { createSecurePair, createServer } = require('node:tls'); +-const pair = createSecurePair(credentials); +-const server = createServer(options); ++const { TLSSocket, createServer } = require('node:tls'); ++const socket = new TLSSocket(underlyingSocket, { secureContext: credentials }); ++const server = createServer(options); +``` + +--- + +### 7) ESM default import +```diff +-import tls from 'node:tls'; +-const pair = tls.createSecurePair(credentials); ++import tls, { TLSSocket } from 'node:tls'; ++const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); +``` + +--- + +### 8) ESM dynamic import (assignment) +```diff +-const tls = await import('node:tls'); +-const pair = tls.createSecurePair(credentials); ++const tls = await import('node:tls'); ++const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); +``` + +--- + +### 9) ESM dynamic import (thenable) +```diff +-import('node:tls').then(tls => { +- const pair = tls.createSecurePair(credentials); +-}); ++import('node:tls').then(tls => { ++ const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); ++}); +``` diff --git a/recipes/tls-create-secure-pair-to-tls-socket/codemod.yaml b/recipes/tls-create-secure-pair-to-tls-socket/codemod.yaml new file mode 100644 index 00000000..978eb8c9 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/codemod.yaml @@ -0,0 +1,24 @@ +schema_version: "1.0" +name: "@nodejs/tls-create-secure-pair-to-tls-socket" +version: 1.0.0 +description: Handle DEP0064 by transforming `createSecurePair` to `TLSSocket` +author: Leonardo Trevizo +license: MIT +workflow: workflow.yaml +category: migration + +targets: + languages: + - javascript + - typescript + +keywords: + - transformation + - migration + - tls + - createSecurePair + - TLSSocket + +registry: + access: public + visibility: public diff --git a/recipes/tls-create-secure-pair-to-tls-socket/package.json b/recipes/tls-create-secure-pair-to-tls-socket/package.json new file mode 100644 index 00000000..88d0efbf --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/package.json @@ -0,0 +1,24 @@ +{ + "name": "@nodejs/tls-create-secure-pair-to-tls-socket", + "version": "1.0.0", + "description": "Handle DEP0064 replacing `tls.createSecurePair()` with `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-create-secure-pair-to-tls-socket", + "bugs": "https://github.com/nodejs/userland-migrations/issues" + }, + "author": "Leo Trevizo", + "license": "MIT", + "homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/tls-create-secure-pair-to-tls-socket/README.md", + "devDependencies": { + "@codemod.com/jssg-types": "^1.0.9" + }, + "dependencies": { + "@nodejs/codemod-utils": "*" + } +} diff --git a/recipes/tls-create-secure-pair-to-tls-socket/src/workflow.ts b/recipes/tls-create-secure-pair-to-tls-socket/src/workflow.ts new file mode 100644 index 00000000..0463cf9e --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/src/workflow.ts @@ -0,0 +1,355 @@ +import type { SgRoot, SgNode, Edit } from '@codemod.com/jssg-types/main'; +import type JS from '@codemod.com/jssg-types/langs/javascript'; +import { resolveBindingPath } from '@nodejs/codemod-utils/ast-grep/resolve-binding-path'; +import { getModuleDependencies } from '@nodejs/codemod-utils/ast-grep/module-dependencies'; + +export default function transform(root: SgRoot): string | null { + const rootNode = root.root(); + const tlsStmts = getModuleDependencies(root, 'tls'); + + if (!tlsStmts.length) return null; + + const cspBindings = collectAllBindings(tlsStmts); + + if (!cspBindings.length) return null; + + const edits: Edit[] = []; + + // Transform all createSecurePair calls + const calls = rootNode.findAll({ rule: { kind: 'call_expression' } }); + + for (const call of calls) { + const callee = call.field('function'); + if (!callee) continue; + + const binding = getCallBinding(callee); + if (!binding || !cspBindings.includes(binding)) continue; + + // Extract arguments + const args = call.field('arguments'); + if (!args) continue; + + const argNodes = args + .children() + .filter((n) => n.kind() !== '(' && n.kind() !== ')' && n.kind() !== ','); + + const options = buildOptions( + argNodes[0]?.text() || null, + argNodes[1]?.text() || null, + argNodes[2]?.text() || null, + argNodes[3]?.text() || null, + ); + + const replacement = binding.includes('.') + ? `new ${binding.replace(/\.createSecurePair$/, '.TLSSocket')}(underlyingSocket, ${options})` + : `new TLSSocket(underlyingSocket, ${options})`; + + edits.push(call.replace(replacement)); + } + + // Rename variables named 'pair' to 'socket' + edits.push(...renamePairVariables(rootNode, cspBindings)); + + // Update imports + edits.push( + ...rewriteTlsImports( + tlsStmts.filter( + (s) => s.is('import_statement') || s.is('variable_declarator'), + ), + ), + ); + + if (!edits.length) return null; + + return rootNode.commitEdits(edits); +} + +function getCallBinding(callee: SgNode): string | null { + if (callee.is('member_expression')) { + const obj = callee.field('object'); + const prop = callee.field('property'); + if (!obj || !prop) return null; + return `${obj.text()}.${prop.text()}`; + } + if (callee.is('identifier')) { + return callee.text(); + } + return null; +} + +function buildOptions( + secureContext?: string | null, + isServer?: string | null, + requestCert?: string | null, + rejectUnauthorized?: string | null, +) { + const kv: string[] = []; + if (secureContext) kv.push(`secureContext: ${secureContext}`); + if (isServer) kv.push(`isServer: ${isServer}`); + if (requestCert) kv.push(`requestCert: ${requestCert}`); + if (rejectUnauthorized) kv.push(`rejectUnauthorized: ${rejectUnauthorized}`); + return kv.length > 0 ? `{ ${kv.join(', ')} }` : '{}'; +} + +function unique(arr: T[]): T[] { + return Array.from(new Set(arr)); +} + +function renamePairVariables(rootNode: SgNode, bindings: string[]): Edit[] { + const edits: Edit[] = []; + + const decls = rootNode.findAll({ + rule: { + kind: 'variable_declarator', + all: [ + { has: { field: 'name', pattern: 'pair' } }, + { has: { field: 'value', kind: 'call_expression' } }, + ], + }, + }); + + for (const decl of decls) { + const callExpr = decl.field('value'); + if (!callExpr) continue; + + const callee = callExpr.field('function'); + if (!callee) continue; + + const binding = getCallBinding(callee); + if (!binding || !bindings.includes(binding)) continue; + + const name = decl.field('name'); + if (name.is('identifier')) { + edits.push(name.replace('socket')); + } + } + + return edits; +} + +function rewriteTlsImports(nodeImportStatements: SgNode[]): Edit[] { + const edits: Edit[] = []; + + for (const stmt of nodeImportStatements) { + if (stmt.is('import_statement')) { + const edit = rewriteEsmImport(stmt); + if (edit) edits.push(edit); + } else if (stmt.is('variable_declarator')) { + const edit = rewriteCjsRequire(stmt); + if (edit) edits.push(edit); + } + } + + return edits; +} + +function rewriteEsmImport(stmt: SgNode): Edit | null { + const named = stmt.find({ rule: { kind: 'named_imports' } }); + const namespace = stmt.find({ rule: { kind: 'namespace_import' } }); + if (!named || namespace) return null; + + const srcText = stmt.field('source')?.text()?.replace(/['"]/g, '') || ''; + if (srcText !== 'tls' && srcText !== 'node:tls') return null; + + const specs = named.findAll({ rule: { kind: 'import_specifier' } }); + if (!specs.some((s) => s.field('name')?.text() === 'createSecurePair')) + return null; + + const kept = specs + .filter((s) => s.field('name')?.text() !== 'createSecurePair') + .map((s) => { + const imported = s.field('name')?.text(); + const alias = s.field('alias')?.text(); + return alias ? `${imported} as ${alias}` : imported; + }); + + if (!kept.includes('TLSSocket')) kept.push('TLSSocket'); + + const defaultImport = stmt + .find({ + rule: { + kind: 'import_clause', + has: { field: 'name', kind: 'identifier' }, + }, + }) + ?.field('name') + ?.text(); + + const rebuilt = defaultImport + ? `import ${defaultImport}, { ${kept.join(', ')} } from '${srcText}';` + : `import { ${kept.join(', ')} } from '${srcText}';`; + + return stmt.replace(rebuilt); +} + +function rewriteCjsRequire(stmt: SgNode): Edit | null { + const name = stmt.field('name'); + if (!name || !name.is('object_pattern')) return null; + + const props = name.findAll({ + rule: { + any: [ + { kind: 'pair_pattern' }, + { kind: 'shorthand_property_identifier_pattern' }, + ], + }, + }); + + const hasCSP = props.some((p) => + p.is('pair_pattern') + ? p.field('key')?.text() === 'createSecurePair' || + p.field('value')?.text() === 'createSecurePair' + : p.text() === 'createSecurePair', + ); + if (!hasCSP) return null; + + const kept = props + .filter((p) => { + const key = p.is('pair_pattern') ? p.field('key')?.text() : p.text(); + return key !== 'createSecurePair'; + }) + .map((p) => { + if (p.is('pair_pattern')) { + const key = p.field('key')?.text(); + const val = p.field('value')?.text(); + return val && val !== key ? `${key}: ${val}` : key; + } + return p.text(); + }); + + if (!kept.includes('TLSSocket')) kept.push('TLSSocket'); + + let decl: SgNode = stmt; + let cur = stmt.parent?.(); + while (cur) { + if (cur.is('lexical_declaration') || cur.is('variable_declaration')) { + decl = cur; + break; + } + cur = cur.parent?.(); + } + + return decl.replace(`const { ${kept.join(', ')} } = require('node:tls');`); +} + +function collectAllBindings(tlsStmts: SgNode[]): string[] { + const bindings: string[] = []; + + for (const stmt of tlsStmts) { + const kind = stmt.kind(); + + // Use resolveBindingPath for standard imports/requires + if ( + kind === 'lexical_declaration' || + kind === 'variable_declarator' || + kind === 'import_statement' || + kind === 'import_clause' + ) { + const resolved = resolveBindingPath( + stmt as unknown as SgNode, + '$.createSecurePair', + ); + if (resolved) bindings.push(resolved); + } + + // Handle ESM aliases: import { createSecurePair as csp } from 'tls' + if (kind === 'import_statement') { + const alias = getEsmAlias(stmt); + if (alias) bindings.push(alias); + } + + // Handle CJS destructured aliases: const { createSecurePair: csp } = require('tls') + if (kind === 'variable_declarator') { + const cjsAlias = getCjsAlias(stmt); + if (cjsAlias) bindings.push(cjsAlias); + + // Handle dynamic imports: const tls = await import('tls') + const awaitBinding = getAwaitImportBinding(stmt); + if (awaitBinding) bindings.push(awaitBinding); + } + + // Handle dynamic import .then(): import('tls').then(tls => ...) + if (kind === 'expression_statement') { + const thenBinding = getThenImportBinding(stmt); + if (thenBinding) bindings.push(thenBinding); + } + } + + return unique(bindings); +} + +function getEsmAlias(stmt: SgNode): string | null { + const specs = stmt.findAll({ + rule: { + kind: 'import_specifier', + has: { field: 'alias', kind: 'identifier' }, + }, + }); + + for (const spec of specs) { + if (spec.field('name')?.text() === 'createSecurePair') { + return spec.field('alias')?.text() || null; + } + } + return null; +} + +function getCjsAlias(stmt: SgNode): string | null { + const objPattern = stmt.field('name'); + if (objPattern?.kind() !== 'object_pattern') return null; + + const pairs = objPattern.findAll({ rule: { kind: 'pair_pattern' } }); + for (const pair of pairs) { + if (pair.field('key')?.text() === 'createSecurePair') { + const alias = pair.field('value')?.text(); + if (alias && alias !== 'createSecurePair') { + return alias; + } + } + } + return null; +} + +function getAwaitImportBinding(stmt: SgNode): string | null { + const value = stmt.field('value'); + if (value?.kind() !== 'await_expression') return null; + + const name = stmt.field('name'); + if (name.is('identifier')) { + return `${name.text()}.createSecurePair`; + } + return null; +} + +function getThenImportBinding(stmt: SgNode): string | null { + const expr = stmt.children().find((c) => c.is('call_expression')); + if (!expr || !expr.is('call_expression')) return null; + + const func = expr.field('function'); + if (func?.kind() !== 'member_expression') return null; + + const prop = func.field('property'); + if (prop?.text() !== 'then') return null; + + const args = expr.field('arguments'); + const callback = args?.find({ + rule: { + any: [{ kind: 'arrow_function' }, { kind: 'function_expression' }], + }, + }); + + if (!callback) return null; + + let param: SgNode | undefined; + if (callback.is('arrow_function')) { + param = + callback.field('parameter') || + callback.field('parameters')?.find({ rule: { kind: 'identifier' } }); + } else if (callback.is('function_expression')) { + param = callback + .field('parameters') + ?.find({ rule: { kind: 'identifier' } }); + } + + return param ? `${param.text()}.createSecurePair` : null; +} diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-destructured-alias.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-destructured-alias.js new file mode 100644 index 00000000..fb233f4f --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-destructured-alias.js @@ -0,0 +1,4 @@ +const { TLSSocket } = require('node:tls'); + +// Using an alias in CJS +const socket = new TLSSocket(underlyingSocket, { secureContext: credentials, isServer: true }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-destructured-basic.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-destructured-basic.js new file mode 100644 index 00000000..aade280a --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-destructured-basic.js @@ -0,0 +1,2 @@ +const { TLSSocket } = require('node:tls'); +const socket = new TLSSocket(underlyingSocket, { secureContext: credentials }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-destructured-flags.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-destructured-flags.js new file mode 100644 index 00000000..7f6653dd --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-destructured-flags.js @@ -0,0 +1,2 @@ +const { TLSSocket } = require('node:tls'); +const socket = new TLSSocket(underlyingSocket, { secureContext: credentials, isServer: true, requestCert: true, rejectUnauthorized: false }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-named.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-named.js new file mode 100644 index 00000000..aade280a --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-named.js @@ -0,0 +1,2 @@ +const { TLSSocket } = require('node:tls'); +const socket = new TLSSocket(underlyingSocket, { secureContext: credentials }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-namespace-basic.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-namespace-basic.js new file mode 100644 index 00000000..a4845d28 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/cjs-namespace-basic.js @@ -0,0 +1,2 @@ +const tls = require('node:tls'); +const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/dynamic-import-assign.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/dynamic-import-assign.js new file mode 100644 index 00000000..c6fd3cea --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/dynamic-import-assign.js @@ -0,0 +1,2 @@ +const tls = await import('node:tls'); +const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/dynamic-import-function.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/dynamic-import-function.js new file mode 100644 index 00000000..f44d4897 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/dynamic-import-function.js @@ -0,0 +1,3 @@ +import('node:tls').then(function (tls) { + const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); +}); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/dynamic-import-then.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/dynamic-import-then.js new file mode 100644 index 00000000..ef1bd895 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/dynamic-import-then.js @@ -0,0 +1,3 @@ +import('node:tls').then(tls => { + const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); +}); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-already-has-tlssocket.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-already-has-tlssocket.js new file mode 100644 index 00000000..51e36fef --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-already-has-tlssocket.js @@ -0,0 +1,5 @@ +import { TLSSocket } from 'node:tls'; + +// Already has TLSSocket in imports +const socket = new TLSSocket(underlyingSocket, { secureContext: credentials }); +const existingSocket = new TLSSocket(socket, {}); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-default-import.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-default-import.js new file mode 100644 index 00000000..941426a9 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-default-import.js @@ -0,0 +1,2 @@ +import tls from 'node:tls'; +const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-mixed-imports.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-mixed-imports.js new file mode 100644 index 00000000..0d18ed04 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-mixed-imports.js @@ -0,0 +1,4 @@ +import tls, { createServer } from 'node:tls'; + +const server = createServer(options); +const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-named-alias.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-named-alias.js new file mode 100644 index 00000000..1306c64d --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-named-alias.js @@ -0,0 +1,4 @@ +import { TLSSocket } from 'node:tls'; + +// Using an alias +const socket = new TLSSocket(underlyingSocket, { secureContext: credentials }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-named-basic.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-named-basic.js new file mode 100644 index 00000000..946f330a --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-named-basic.js @@ -0,0 +1,2 @@ +import { TLSSocket } from 'node:tls'; +const socket = new TLSSocket(underlyingSocket, { secureContext: credentials }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-namespace-basic.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-namespace-basic.js new file mode 100644 index 00000000..9255483a --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/esm-namespace-basic.js @@ -0,0 +1,2 @@ +import * as tls from 'node:tls'; +const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/mixed-with-other-symbols.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/mixed-with-other-symbols.js new file mode 100644 index 00000000..ae0e16c1 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/mixed-with-other-symbols.js @@ -0,0 +1,3 @@ +const { createServer, TLSSocket } = require('node:tls'); +const socket = new TLSSocket(underlyingSocket, { secureContext: credentials }); +const server = createServer(options); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/multiple-calls.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/multiple-calls.js new file mode 100644 index 00000000..986cccdf --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/multiple-calls.js @@ -0,0 +1,8 @@ +const { TLSSocket } = require('node:tls'); + +// Multiple calls with different arguments +const pair1 = new TLSSocket(underlyingSocket, {}); +const pair2 = new TLSSocket(underlyingSocket, { secureContext: credentials }); +const pair3 = new TLSSocket(underlyingSocket, { secureContext: credentials, isServer: true }); +const pair4 = new TLSSocket(underlyingSocket, { secureContext: credentials, isServer: true, requestCert: false }); +const pair5 = new TLSSocket(underlyingSocket, { secureContext: credentials, isServer: true, requestCert: false, rejectUnauthorized: true }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/nested-calls.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/nested-calls.js new file mode 100644 index 00000000..463f0081 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/nested-calls.js @@ -0,0 +1,12 @@ +const tls = require('node:tls'); + +function setupTLS() { + const socket = new tls.TLSSocket(underlyingSocket, { secureContext: credentials }); + return pair; +} + +class TLSManager { + init() { + this.pair = new tls.TLSSocket(underlyingSocket, { secureContext: credentials, isServer: true }); + } +} diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/no-changes-needed.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/no-changes-needed.js new file mode 100644 index 00000000..28cc6216 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/no-changes-needed.js @@ -0,0 +1,4 @@ +const { TLSSocket } = require('node:tls'); + +// Code that already uses TLSSocket - should not change +const socket = new TLSSocket(underlyingSocket, { secureContext: credentials }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/not-from-tls.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/not-from-tls.js new file mode 100644 index 00000000..8e3f80cf --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/expected/not-from-tls.js @@ -0,0 +1,3 @@ +// Without tls module - should not transform +const createSecurePair = someOtherModule.createSecurePair; +const pair = createSecurePair(credentials); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-destructured-alias.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-destructured-alias.js new file mode 100644 index 00000000..b6236075 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-destructured-alias.js @@ -0,0 +1,4 @@ +const { createSecurePair: csp } = require('node:tls'); + +// Using an alias in CJS +const pair = csp(credentials, true); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-destructured-basic.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-destructured-basic.js new file mode 100644 index 00000000..85389cf3 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-destructured-basic.js @@ -0,0 +1,2 @@ +const { createSecurePair } = require('node:tls'); +const pair = createSecurePair(credentials); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-destructured-flags.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-destructured-flags.js new file mode 100644 index 00000000..33c62eb0 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-destructured-flags.js @@ -0,0 +1,2 @@ +const { createSecurePair } = require('node:tls'); +const pair = createSecurePair(credentials, true, true, false); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-named.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-named.js new file mode 100644 index 00000000..85389cf3 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-named.js @@ -0,0 +1,2 @@ +const { createSecurePair } = require('node:tls'); +const pair = createSecurePair(credentials); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-namespace-basic.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-namespace-basic.js new file mode 100644 index 00000000..4ad73152 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/cjs-namespace-basic.js @@ -0,0 +1,2 @@ +const tls = require('node:tls'); +const pair = tls.createSecurePair(credentials); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/dynamic-import-assign.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/dynamic-import-assign.js new file mode 100644 index 00000000..fe969fef --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/dynamic-import-assign.js @@ -0,0 +1,2 @@ +const tls = await import('node:tls'); +const pair = tls.createSecurePair(credentials); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/dynamic-import-function.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/dynamic-import-function.js new file mode 100644 index 00000000..ce198886 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/dynamic-import-function.js @@ -0,0 +1,3 @@ +import('node:tls').then(function (tls) { + const pair = tls.createSecurePair(credentials); +}); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/dynamic-import-then.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/dynamic-import-then.js new file mode 100644 index 00000000..6ee9842f --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/dynamic-import-then.js @@ -0,0 +1,3 @@ +import('node:tls').then(tls => { + const pair = tls.createSecurePair(credentials); +}); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-already-has-tlssocket.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-already-has-tlssocket.js new file mode 100644 index 00000000..34889666 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-already-has-tlssocket.js @@ -0,0 +1,5 @@ +import { createSecurePair, TLSSocket } from 'node:tls'; + +// Already has TLSSocket in imports +const pair = createSecurePair(credentials); +const existingSocket = new TLSSocket(socket, {}); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-default-import.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-default-import.js new file mode 100644 index 00000000..3b456881 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-default-import.js @@ -0,0 +1,2 @@ +import tls from 'node:tls'; +const pair = tls.createSecurePair(credentials); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-mixed-imports.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-mixed-imports.js new file mode 100644 index 00000000..b115e395 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-mixed-imports.js @@ -0,0 +1,4 @@ +import tls, { createServer } from 'node:tls'; + +const server = createServer(options); +const pair = tls.createSecurePair(credentials); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-named-alias.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-named-alias.js new file mode 100644 index 00000000..cfc1085c --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-named-alias.js @@ -0,0 +1,4 @@ +import { createSecurePair as csp } from 'node:tls'; + +// Using an alias +const pair = csp(credentials); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-named-basic.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-named-basic.js new file mode 100644 index 00000000..9dccd7ed --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-named-basic.js @@ -0,0 +1,2 @@ +import { createSecurePair } from 'node:tls'; +const pair = createSecurePair(credentials); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-namespace-basic.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-namespace-basic.js new file mode 100644 index 00000000..28119184 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/esm-namespace-basic.js @@ -0,0 +1,2 @@ +import * as tls from 'node:tls'; +const pair = tls.createSecurePair(credentials); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/mixed-with-other-symbols.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/mixed-with-other-symbols.js new file mode 100644 index 00000000..e6a341db --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/mixed-with-other-symbols.js @@ -0,0 +1,3 @@ +const { createSecurePair, createServer } = require('node:tls'); +const pair = createSecurePair(credentials); +const server = createServer(options); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/multiple-calls.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/multiple-calls.js new file mode 100644 index 00000000..4e93d447 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/multiple-calls.js @@ -0,0 +1,8 @@ +const { createSecurePair } = require('node:tls'); + +// Multiple calls with different arguments +const pair1 = createSecurePair(); +const pair2 = createSecurePair(credentials); +const pair3 = createSecurePair(credentials, true); +const pair4 = createSecurePair(credentials, true, false); +const pair5 = createSecurePair(credentials, true, false, true); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/nested-calls.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/nested-calls.js new file mode 100644 index 00000000..ec7f1b0c --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/nested-calls.js @@ -0,0 +1,12 @@ +const tls = require('node:tls'); + +function setupTLS() { + const pair = tls.createSecurePair(credentials); + return pair; +} + +class TLSManager { + init() { + this.pair = tls.createSecurePair(credentials, true); + } +} diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/no-changes-needed.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/no-changes-needed.js new file mode 100644 index 00000000..28cc6216 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/no-changes-needed.js @@ -0,0 +1,4 @@ +const { TLSSocket } = require('node:tls'); + +// Code that already uses TLSSocket - should not change +const socket = new TLSSocket(underlyingSocket, { secureContext: credentials }); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/tests/input/not-from-tls.js b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/not-from-tls.js new file mode 100644 index 00000000..8e3f80cf --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/tests/input/not-from-tls.js @@ -0,0 +1,3 @@ +// Without tls module - should not transform +const createSecurePair = someOtherModule.createSecurePair; +const pair = createSecurePair(credentials); diff --git a/recipes/tls-create-secure-pair-to-tls-socket/workflow.yaml b/recipes/tls-create-secure-pair-to-tls-socket/workflow.yaml new file mode 100644 index 00000000..1b82d8a3 --- /dev/null +++ b/recipes/tls-create-secure-pair-to-tls-socket/workflow.yaml @@ -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: Handle DEP0064 via replacing deprecated `tls.createSecurePair()` to `tls.Socket`. + js-ast-grep: + js_file: src/workflow.ts + base_path: . + include: + - "**/*.js" + - "**/*.jsx" + - "**/*.mjs" + - "**/*.cjs" + - "**/*.cts" + - "**/*.mts" + - "**/*.ts" + - "**/*.tsx" + exclude: + - "**/node_modules/**" + language: typescript