diff --git a/package-lock.json b/package-lock.json index 802c1bd9..d7c74b9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1505,6 +1505,10 @@ "resolved": "recipes/rmdir", "link": true }, + "node_modules/@nodejs/slow-buffer-to-buffer-alloc-unsafe-slow": { + "resolved": "recipes/slow-buffer-to-buffer-alloc-unsafe-slow", + "link": true + }, "node_modules/@nodejs/tmpdir-to-tmpdir": { "resolved": "recipes/tmpdir-to-tmpdir", "link": true @@ -4372,6 +4376,17 @@ "@codemod.com/jssg-types": "^1.0.9" } }, + "recipes/slow-buffer-to-buffer-alloc-unsafe-slow": { + "name": "@nodejs/slow-buffer-to-buffer-alloc-unsafe-slow", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@nodejs/codemod-utils": "*" + }, + "devDependencies": { + "@codemod.com/jssg-types": "^1.0.3" + } + }, "recipes/tmpdir-to-tmpdir": { "name": "@nodejs/tmpdir-to-tmpdir", "version": "1.0.0", diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/README.md b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/README.md new file mode 100644 index 00000000..9f1889dd --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/README.md @@ -0,0 +1,27 @@ +# SlowBuffer to Buffer.allocUnsafeSlow Codemod + +This codemod migrates deprecated `SlowBuffer` usage to `Buffer.allocUnsafeSlow()` to handle Node.js [DEP0030](https://nodejs.org/api/deprecations.html#DEP0030). + +## What it does + +This codemod transforms: + +1. `SlowBuffer` constructor calls to `Buffer.allocUnsafeSlow()` +2. Direct `SlowBuffer` calls to `Buffer.allocUnsafeSlow()` +3. Import/require statements be synced with new function + +## Example + +**Before:** + +```javascript +import { SlowBuffer } from "buffer"; +const buf = new SlowBuffer(1024); +``` + +**After:** + +```javascript +import { Buffer } from "buffer"; +const buf = Buffer.allocUnsafeSlow(1024); +``` diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/codemod.yaml b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/codemod.yaml new file mode 100644 index 00000000..16d36501 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/codemod.yaml @@ -0,0 +1,23 @@ +schema_version: "1.0" +name: "@nodejs/slow-buffer-to-buffer-alloc-unsafe-slow" +version: "1.0.0" +description: Handle DEP0030 via transforming SlowBuffer usage to Buffer.allocUnsafeSlow(). +author: lluisemper(Lluis Semper Lloret) +license: MIT +workflow: workflow.yaml +category: migration + +targets: + languages: + - javascript + - typescript + +keywords: + - transformation + - migration + - buffer + - slowbuffer + +registry: + access: public + visibility: public diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/package.json b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/package.json new file mode 100644 index 00000000..cd4666cd --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/package.json @@ -0,0 +1,24 @@ +{ + "name": "@nodejs/slow-buffer-to-buffer-alloc-unsafe-slow", + "version": "1.0.0", + "description": "Handle DEP0030 via transforming SlowBuffer usage to Buffer.allocUnsafeSlow().", + "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/slow-buffer-to-buffer-alloc-unsafe-slow", + "bugs": "https://github.com/nodejs/userland-migrations/issues" + }, + "author": "lluisemper(Lluis Semper Lloret)", + "license": "MIT", + "homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/README.md", + "dependencies": { + "@nodejs/codemod-utils": "*" + }, + "devDependencies": { + "@codemod.com/jssg-types": "^1.0.3" + } +} diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/src/workflow.ts b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/src/workflow.ts new file mode 100644 index 00000000..c0e9ac43 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/src/workflow.ts @@ -0,0 +1,288 @@ +import type { Edit, SgRoot, SgNode } from "@codemod.com/jssg-types/main"; +import { + getNodeImportStatements, + getNodeImportCalls, +} from "@nodejs/codemod-utils/ast-grep/import-statement"; +import { getNodeRequireCalls } from "@nodejs/codemod-utils/ast-grep/require-call"; +import { resolveBindingPath } from "@nodejs/codemod-utils/ast-grep/resolve-binding-path"; +import { updateBinding } from "@nodejs/codemod-utils/ast-grep/update-binding"; + +type StatementType = "import-dynamic" | "import-static" | "require"; + +const nodeGetterMap = { + "import-dynamic": getNodeImportCalls, + require: getNodeRequireCalls, +} as const; + +/** + * Main entry point that orchestrates all SlowBuffer → Buffer transformations + * @param root - The AST root node to transform + * @returns The transformed code as a string, or null if no changes were made + */ +export default function transform(root: SgRoot): string | null { + const rootNode = root.root(); + const edits: Edit[] = []; + + // Process transformations in order: + // 1. CommonJS require statements + processStatements(root, edits, "require"); + // 2. ESM import statements + processStatements(root, edits, "import-static"); + // 3. Dynamic import statements + processStatements(root, edits, "import-dynamic"); + // 4. Usage patterns (constructor/function calls) + processSlowBufferUsage(rootNode, edits); + + if (!edits.length) return null; + return rootNode.commitEdits(edits); +} + +/** + * Unified function to process different types of import/require statements + * @param root - The AST root to search for statements + * @param edits - Array to collect edit operations + * @param type - The type of statement to process + */ +function processStatements(root: SgRoot, edits: Edit[], type: StatementType): void { + if (type === "import-static") { + processESMImports(root, edits); + return; + } + + const statements = nodeGetterMap[type](root, "buffer"); + + for (const statement of statements) { + const nameField = statement.field("name"); + if (!nameField) continue; + + if (nameField.kind() === "identifier") { + processIdentifierPattern(statement, nameField, edits, type); + } else if (nameField.kind() === "object_pattern") { + processObjectPattern(statement, nameField, edits); + } + } +} + +/** + * Handle ESM import statements + */ +function processESMImports(root: SgRoot, edits: Edit[]): void { + const importStatements = getNodeImportStatements(root, "buffer"); + + for (const statement of importStatements) { + const allSpecifiers = statement.findAll({ rule: { kind: "import_specifier" } }); + + const hasBuffer = allSpecifiers.some((spec) => spec.child(0)?.text() === "Buffer"); + const slowBufferSpecs = allSpecifiers.filter((spec) => spec.child(0)?.text() === "SlowBuffer"); + + for (const spec of slowBufferSpecs) { + const imported = spec.child(0); + const alias = spec.child(2); // "as alias" part + + if (alias) { + // SlowBuffer as SomeAlias -> Buffer as SomeAlias + edits.push(imported.replace("Buffer")); + } else if (hasBuffer) { + // Remove SlowBuffer when Buffer already exists + applyUpdateBinding(statement, edits, { old: "SlowBuffer" }); + } else { + // SlowBuffer -> Buffer + applyUpdateBinding(statement, edits, { old: "SlowBuffer", new: "Buffer" }); + } + } + } +} + +/** + * Handle identifier patterns like: const SlowBuffer = require('buffer') + */ +function processIdentifierPattern( + statement: SgNode, + nameField: SgNode, + edits: Edit[], + type: StatementType, +): void { + if (nameField.text() !== "SlowBuffer") return; + + if (type === "require") { + // Handle: const SlowBuffer = require('buffer').SlowBuffer + const valueField = statement.field("value"); + if (valueField?.kind() === "member_expression") { + const property = valueField.field("property"); + if (property?.text() === "SlowBuffer") { + edits.push(nameField.replace("Buffer")); + edits.push(property.replace("Buffer")); + return; + } + } + } + + // Handle: const SlowBuffer = await import('buffer') + edits.push(nameField.replace("Buffer")); +} + +/** + * Handle object destructuring patterns + */ +function processObjectPattern(statement: SgNode, nameField: SgNode, edits: Edit[]): void { + // Check for aliased patterns: { SlowBuffer: alias } + const aliasedPatterns = nameField.findAll({ + rule: { + kind: "pair_pattern", + has: { + field: "key", + kind: "property_identifier", + regex: "^SlowBuffer$", + }, + }, + }); + + if (aliasedPatterns.length > 0) { + // Just replace the key: { SlowBuffer: SB } -> { Buffer: SB } + for (const pattern of aliasedPatterns) { + const key = pattern.field("key"); + if (key?.text() === "SlowBuffer") { + edits.push(key.replace("Buffer")); + } + } + return; + } + + // Check for shorthand patterns: { SlowBuffer } + const hasSlowBufferShorthand = nameField + .findAll({ rule: { kind: "shorthand_property_identifier_pattern" } }) + .some((n) => n.text() === "SlowBuffer"); + + if (!hasSlowBufferShorthand) return; + + const hasBufferShorthand = nameField + .findAll({ rule: { kind: "shorthand_property_identifier_pattern" } }) + .some((n) => n.text() === "Buffer"); + + const parentDeclaration = findParentDeclaration(statement); + if (!parentDeclaration) return; + + if (hasBufferShorthand) { + // Remove SlowBuffer when Buffer exists + applyUpdateBinding(parentDeclaration, edits, { old: "SlowBuffer" }); + } else { + // Replace SlowBuffer with Buffer + applyUpdateBinding(parentDeclaration, edits, { old: "SlowBuffer", new: "Buffer" }); + } +} + +/** + * Apply updateBinding result to edits array + */ +function applyUpdateBinding( + node: SgNode, + edits: Edit[], + options: { old?: string; new?: string }, +): void { + const result = updateBinding(node, options); + if (result?.edit) { + edits.push(result.edit); + } else if (result?.lineToRemove) { + edits.push(node.replace("")); + } +} + +/** + * Find the parent lexical_declaration node for updateBinding + */ +function findParentDeclaration(node: SgNode): SgNode | null { + let current = node; + while (current) { + const kind = current.kind(); + if (kind === "lexical_declaration" || kind === "variable_declaration") { + return current; + } + current = current.parent(); + if (!current) break; + } + return null; +} + +/** + * Extract arguments from a call expression + */ +function extractArgs(match: SgNode): string { + try { + const argsMatches = match.getMultipleMatches("ARGS"); + if (argsMatches.length > 0) { + return argsMatches.map((a) => a.text()).join(", "); + } + } catch { + // Fall through to field-based extraction + } + + const argsField = match.field("arguments"); + if (argsField) { + const text = argsField.text(); + return text.slice(1, -1); // Remove parentheses + } + + return ""; +} + +/** + * Transform SlowBuffer usage to Buffer.allocUnsafeSlow + */ +function transformSlowBufferCall(match: SgNode, binding: string, edits: Edit[]): void { + const args = extractArgs(match); + const replacement = + binding === "SlowBuffer" + ? `Buffer.allocUnsafeSlow(${args})` + : `${binding}.allocUnsafeSlow(${args})`; + + edits.push(match.replace(replacement)); +} + +/** + * Process SlowBuffer constructor and function calls + */ +function processSlowBufferUsage(rootNode: SgNode, edits: Edit[]): void { + const root = rootNode.getRoot(); + const allStatements = [ + ...getNodeImportStatements(root, "buffer"), + ...getNodeRequireCalls(root, "buffer"), + ...getNodeImportCalls(root, "buffer"), + ]; + + // Process bound SlowBuffer calls (from imports/requires) + for (const importNode of allStatements) { + try { + const binding = resolveBindingPath(importNode, "$.SlowBuffer"); + if (!binding) continue; + + const slowBufferCalls = rootNode.findAll({ + rule: { + any: [ + { kind: "new_expression", pattern: `new ${binding}($$$ARGS)` }, + { kind: "call_expression", pattern: `${binding}($$$ARGS)` }, + ], + }, + }); + + for (const match of slowBufferCalls) { + transformSlowBufferCall(match, binding, edits); + } + } catch { + // Skip if binding resolution fails + } + } + + // Process direct SlowBuffer calls (unbound) + const directCalls = rootNode.findAll({ + rule: { + any: [ + { kind: "new_expression", pattern: "new SlowBuffer($$$ARGS)" }, + { kind: "call_expression", pattern: "SlowBuffer($$$ARGS)" }, + ], + }, + }); + + for (const match of directCalls) { + transformSlowBufferCall(match, "SlowBuffer", edits); + } +} diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-01.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-01.js new file mode 100644 index 00000000..76202afd --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-01.js @@ -0,0 +1,5 @@ +const Buffer = require('buffer').Buffer; + +// Using SlowBuffer constructor +const buf1 = Buffer.allocUnsafeSlow(1024); +const buf2 = Buffer.allocUnsafeSlow(512); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-02.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-02.js new file mode 100644 index 00000000..3ece8ba1 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-02.js @@ -0,0 +1,3 @@ +// Direct import +const { Buffer } = require('buffer'); +const buf3 = Buffer.allocUnsafeSlow(256); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-03.mjs b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-03.mjs new file mode 100644 index 00000000..0a6fd3d3 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-03.mjs @@ -0,0 +1,2 @@ +import { Buffer } from 'buffer'; +const buf = Buffer.allocUnsafeSlow(1024); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-04.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-04.js new file mode 100644 index 00000000..fecf6ab2 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-04.js @@ -0,0 +1,4 @@ +// Mixed with existing Buffer import +const { Buffer } = require('buffer'); +const buf1 = Buffer.allocUnsafeSlow(100); +const buf2 = Buffer.alloc(200); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-05.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-05.js new file mode 100644 index 00000000..7540c016 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-05.js @@ -0,0 +1,8 @@ +// Multiple SlowBuffer calls +const { Buffer } = require('buffer'); + +function createBuffers() { + const buf1 = Buffer.allocUnsafeSlow(1024); + const buf2 = Buffer.allocUnsafeSlow(512); + return [buf1, buf2]; +} \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-06.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-06.js new file mode 100644 index 00000000..c61c98e2 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-06.js @@ -0,0 +1,10 @@ +// Complex destructuring patterns with SlowBuffer +const { Buffer, constants } = require('buffer'); +const { Buffer: SB } = require('buffer'); +const { Buffer: SlowBuf, Buffer: Buf } = require('buffer'); + +// Various usage patterns +const buf1 = Buffer.allocUnsafeSlow(1024); +const buf2 = Buffer.allocUnsafeSlow(512); +const buf3 = SB.allocUnsafeSlow(256); +const buf4 = SlowBuf.allocUnsafeSlow(128); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-07.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-07.js new file mode 100644 index 00000000..7763d97c --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-07.js @@ -0,0 +1,19 @@ +// Multiple arguments and expressions +const { Buffer } = require('buffer'); + +// SlowBuffer with complex expressions +const size = 1024; +const buf1 = Buffer.allocUnsafeSlow(size * 2); +const buf2 = Buffer.allocUnsafeSlow(Math.max(100, size)); +const buf3 = Buffer.allocUnsafeSlow(size + 512); +const buf4 = Buffer.allocUnsafeSlow(parseInt('256')); + +// SlowBuffer in nested contexts +function createBuffer(size) { + return Buffer.allocUnsafeSlow(size); +} + +const buffers = [ + Buffer.allocUnsafeSlow(64), + Buffer.allocUnsafeSlow(128), +]; \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-08.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-08.js new file mode 100644 index 00000000..21c3228e --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-08.js @@ -0,0 +1,10 @@ +// Edge case: Just SlowBuffer in destructuring +const { Buffer } = require('buffer'); + +// Edge case: SlowBuffer at different positions +const { Buffer, constants } = require('buffer'); +const { constants, Buffer } = require('buffer'); + +// Edge case: Multiple SlowBuffer references (should only add Buffer once) +const buf1 = Buffer.allocUnsafeSlow(100); +const buf2 = Buffer.allocUnsafeSlow(200); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-09.mjs b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-09.mjs new file mode 100644 index 00000000..2eaabc65 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-09.mjs @@ -0,0 +1,10 @@ +// ESM import edge cases +import { Buffer } from 'buffer'; +import { Buffer as SB, Buffer as B } from 'buffer'; +import { constants, Buffer } from 'buffer'; + +// Various usage patterns with imports +const buf1 = Buffer.allocUnsafeSlow(1024); +const buf2 = Buffer.allocUnsafeSlow(512); +const buf3 = SB.allocUnsafeSlow(256); +const buf4 = SB.allocUnsafeSlow(128); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-10.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-10.js new file mode 100644 index 00000000..38ddd7a7 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-10.js @@ -0,0 +1,14 @@ +// Comments and whitespace edge cases +const { Buffer } = require('buffer'); + +// Using SlowBuffer constructor - should be updated +const buf1 = Buffer.allocUnsafeSlow(1024); // inline comment about SlowBuffer +/* + * Multi-line comment mentioning SlowBuffer + * This should not be changed by the codemod + */ +const buf2 = Buffer.allocUnsafeSlow(512); + +// Edge case: SlowBuffer in string (should not be changed) +const message = "This mentions SlowBuffer but should not change"; +const code = 'new SlowBuffer(100)'; // This is in a string \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-11.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-11.js new file mode 100644 index 00000000..55792504 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-11.js @@ -0,0 +1,10 @@ +// No arguments edge case +const { Buffer } = require('buffer'); + +// SlowBuffer with no arguments (edge case) +const buf1 = Buffer.allocUnsafeSlow(); +const buf2 = Buffer.allocUnsafeSlow(); + +// SlowBuffer with empty parentheses and whitespace +const buf3 = Buffer.allocUnsafeSlow( ); +const buf4 = Buffer.allocUnsafeSlow( ); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-12.mjs b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-12.mjs new file mode 100644 index 00000000..c145b6a8 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-12.mjs @@ -0,0 +1,19 @@ +// Dynamic imports with SlowBuffer +const { Buffer } = await import('buffer'); +const { Buffer: SB, Buffer } = await import('buffer'); +const buffer = await import('buffer'); + +// Usage patterns with dynamic imports +const buf1 = Buffer.allocUnsafeSlow(1024); +const buf2 = Buffer.allocUnsafeSlow(512); +const buf3 = SB.allocUnsafeSlow(256); +const buf4 = SB.allocUnsafeSlow(128); + +// Mixed with regular imports +import { constants } from 'buffer'; +const { Buffer: DynamicSB } = await import('buffer'); +const buf5 = DynamicSB.allocUnsafeSlow(64); + +// Direct usage from buffer module (these patterns are handled by member access, not dynamic imports) +const buf6 = buffer.SlowBuffer.allocUnsafeSlow(32); +const buf7 = buffer.SlowBuffer.allocUnsafeSlow(16); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-13.mjs b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-13.mjs new file mode 100644 index 00000000..b8eee059 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/expected/file-13.mjs @@ -0,0 +1,3 @@ +// Simple dynamic import test case +const { Buffer: SB, Buffer } = await import('buffer'); +const buf1 = SB.allocUnsafeSlow(100); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-01.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-01.js new file mode 100644 index 00000000..f6730456 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-01.js @@ -0,0 +1,5 @@ +const SlowBuffer = require('buffer').SlowBuffer; + +// Using SlowBuffer constructor +const buf1 = new SlowBuffer(1024); +const buf2 = SlowBuffer(512); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-02.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-02.js new file mode 100644 index 00000000..1d935d2c --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-02.js @@ -0,0 +1,3 @@ +// Direct import +const { SlowBuffer } = require('buffer'); +const buf3 = new SlowBuffer(256); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-03.mjs b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-03.mjs new file mode 100644 index 00000000..aaf8a3ca --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-03.mjs @@ -0,0 +1,2 @@ +import { SlowBuffer } from 'buffer'; +const buf = new SlowBuffer(1024); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-04.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-04.js new file mode 100644 index 00000000..144f5999 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-04.js @@ -0,0 +1,4 @@ +// Mixed with existing Buffer import +const { Buffer, SlowBuffer } = require('buffer'); +const buf1 = new SlowBuffer(100); +const buf2 = Buffer.alloc(200); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-05.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-05.js new file mode 100644 index 00000000..c51ea61e --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-05.js @@ -0,0 +1,8 @@ +// Multiple SlowBuffer calls +const { SlowBuffer } = require('buffer'); + +function createBuffers() { + const buf1 = new SlowBuffer(1024); + const buf2 = SlowBuffer(512); + return [buf1, buf2]; +} \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-06.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-06.js new file mode 100644 index 00000000..94db1d99 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-06.js @@ -0,0 +1,10 @@ +// Complex destructuring patterns with SlowBuffer +const { SlowBuffer, Buffer, constants } = require('buffer'); +const { SlowBuffer: SB } = require('buffer'); +const { SlowBuffer: SlowBuf, Buffer: Buf } = require('buffer'); + +// Various usage patterns +const buf1 = new SlowBuffer(1024); +const buf2 = SlowBuffer(512); +const buf3 = new SB(256); +const buf4 = SlowBuf(128); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-07.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-07.js new file mode 100644 index 00000000..74fa0fcc --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-07.js @@ -0,0 +1,19 @@ +// Multiple arguments and expressions +const { SlowBuffer } = require('buffer'); + +// SlowBuffer with complex expressions +const size = 1024; +const buf1 = new SlowBuffer(size * 2); +const buf2 = SlowBuffer(Math.max(100, size)); +const buf3 = new SlowBuffer(size + 512); +const buf4 = SlowBuffer(parseInt('256')); + +// SlowBuffer in nested contexts +function createBuffer(size) { + return new SlowBuffer(size); +} + +const buffers = [ + new SlowBuffer(64), + SlowBuffer(128), +]; \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-08.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-08.js new file mode 100644 index 00000000..d66edf3a --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-08.js @@ -0,0 +1,10 @@ +// Edge case: Just SlowBuffer in destructuring +const { SlowBuffer } = require('buffer'); + +// Edge case: SlowBuffer at different positions +const { Buffer, SlowBuffer, constants } = require('buffer'); +const { constants, SlowBuffer, Buffer } = require('buffer'); + +// Edge case: Multiple SlowBuffer references (should only add Buffer once) +const buf1 = new SlowBuffer(100); +const buf2 = SlowBuffer(200); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-09.mjs b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-09.mjs new file mode 100644 index 00000000..416e6856 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-09.mjs @@ -0,0 +1,10 @@ +// ESM import edge cases +import { SlowBuffer, Buffer } from 'buffer'; +import { SlowBuffer as SB, Buffer as B } from 'buffer'; +import { constants, SlowBuffer } from 'buffer'; + +// Various usage patterns with imports +const buf1 = new SlowBuffer(1024); +const buf2 = SlowBuffer(512); +const buf3 = new SB(256); +const buf4 = SB(128); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-10.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-10.js new file mode 100644 index 00000000..fd841995 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-10.js @@ -0,0 +1,14 @@ +// Comments and whitespace edge cases +const { SlowBuffer } = require('buffer'); + +// Using SlowBuffer constructor - should be updated +const buf1 = new SlowBuffer(1024); // inline comment about SlowBuffer +/* + * Multi-line comment mentioning SlowBuffer + * This should not be changed by the codemod + */ +const buf2 = SlowBuffer(512); + +// Edge case: SlowBuffer in string (should not be changed) +const message = "This mentions SlowBuffer but should not change"; +const code = 'new SlowBuffer(100)'; // This is in a string \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-11.js b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-11.js new file mode 100644 index 00000000..ef12d850 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-11.js @@ -0,0 +1,10 @@ +// No arguments edge case +const { SlowBuffer } = require('buffer'); + +// SlowBuffer with no arguments (edge case) +const buf1 = new SlowBuffer(); +const buf2 = SlowBuffer(); + +// SlowBuffer with empty parentheses and whitespace +const buf3 = new SlowBuffer( ); +const buf4 = SlowBuffer( ); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-12.mjs b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-12.mjs new file mode 100644 index 00000000..c716986a --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-12.mjs @@ -0,0 +1,19 @@ +// Dynamic imports with SlowBuffer +const { SlowBuffer } = await import('buffer'); +const { SlowBuffer: SB, Buffer } = await import('buffer'); +const buffer = await import('buffer'); + +// Usage patterns with dynamic imports +const buf1 = new SlowBuffer(1024); +const buf2 = SlowBuffer(512); +const buf3 = new SB(256); +const buf4 = SB(128); + +// Mixed with regular imports +import { constants } from 'buffer'; +const { SlowBuffer: DynamicSB } = await import('buffer'); +const buf5 = DynamicSB(64); + +// Direct usage from buffer module (these patterns are handled by member access, not dynamic imports) +const buf6 = new buffer.SlowBuffer(32); +const buf7 = buffer.SlowBuffer(16); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-13.mjs b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-13.mjs new file mode 100644 index 00000000..ceaac706 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/tests/input/file-13.mjs @@ -0,0 +1,3 @@ +// Simple dynamic import test case +const { SlowBuffer: SB, Buffer } = await import('buffer'); +const buf1 = new SB(100); \ No newline at end of file diff --git a/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/workflow.yaml b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/workflow.yaml new file mode 100644 index 00000000..64b66b92 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/workflow.yaml @@ -0,0 +1,24 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/codemod/codemod/refs/heads/main/schemas/workflow.json + +version: "1.0" +nodes: + - id: apply-transforms + name: Apply AST Transformations + type: automatic + steps: + - name: Handle DEP0030 via transforming SlowBuffer usage to Buffer.allocUnsafeSlow().. + js-ast-grep: + js_file: src/workflow.ts + base_path: . + include: + - "**/*.js" + - "**/*.jsx" + - "**/*.mjs" + - "**/*.cjs" + - "**/*.cts" + - "**/*.mts" + - "**/*.ts" + - "**/*.tsx" + exclude: + - "**/node_modules/**" + language: typescript