diff --git a/package-lock.json b/package-lock.json index 4c7e22d5..19275c8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -196,6 +196,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -212,6 +213,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -228,6 +230,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -244,6 +247,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -260,6 +264,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -276,6 +281,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -292,6 +298,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -308,6 +315,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -324,6 +332,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1427,6 +1436,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 @@ -4116,6 +4129,17 @@ "@codemod.com/jssg-types": "^1.0.3" } }, + "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..520a1675 --- /dev/null +++ b/recipes/slow-buffer-to-buffer-alloc-unsafe-slow/src/workflow.ts @@ -0,0 +1,330 @@ +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"; + +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") { + // ESM imports have a different structure - handle them separately + for (const imp of root.root().findAll({ rule: { kind: "import_clause" } })) { + const specifiers = imp.findAll({ rule: { kind: "import_specifier" } }); + + // Collect all imported names (not aliases) to check for conflicts + const importedNames = new Set(); + for (const spec of specifiers) { + const imported = spec.child(0); // imported name + if (imported) { + importedNames.add(imported.text()); + } + } + + for (const spec of specifiers) { + const imported = spec.child(0); // imported name + if (!imported) continue; + + if (imported.text() === "SlowBuffer") { + // Check if this specifier has an alias + const alias = spec.child(2); // "as alias" part + + if (alias) { + // Has alias: SlowBuffer as SomeAlias -> Buffer as SomeAlias + edits.push(imported.replace("Buffer")); + } else if (importedNames.has("Buffer")) { + // No alias but Buffer already imported: remove SlowBuffer entirely using AST + removeSlowBufferFromImport(imp, edits); + } else { + // No alias and no Buffer conflict: SlowBuffer -> Buffer + edits.push(imported.replace("Buffer")); + } + } + } + } + return; + } + + const statements = nodeGetterMap[type](root, "buffer"); + + for (const statement of statements) { + const nameField = statement.field("name"); + if (!nameField) continue; + + // Handle different name field patterns + switch (nameField.kind()) { + case "identifier": + if (nameField.text() === "SlowBuffer") { + if (type === "require") { + // Handle direct assignment: const SlowBuffer = require('buffer').SlowBuffer + const valueField = statement.field("value"); + if (valueField && valueField.kind() === "member_expression") { + const property = valueField.field("property"); + if (property && property.text() === "SlowBuffer") { + // Transform both the variable name and the property access + edits.push(nameField.replace("Buffer")); + edits.push(property.replace("Buffer")); + } + } + } else { + // Handle dynamic import: const SlowBuffer = await import('buffer') + edits.push(nameField.replace("Buffer")); + } + } + break; + case "object_pattern": + // Handle destructuring: const { SlowBuffer, ... } = require('buffer') or import('buffer') + transformObjectPattern(nameField, edits); + break; + } + } +} + +/** + * Transforms object destructuring patterns containing SlowBuffer references + * Handles complex cases like: + * - { SlowBuffer } → { Buffer } + * - { SlowBuffer, Buffer } → { Buffer } (removes duplicate) + * - { SlowBuffer: alias } → { Buffer: alias } + * - { SlowBuffer as alias } → { Buffer as alias } (ESM) + * @param patternNode - AST node representing the destructuring pattern + * @param edits - Array to collect edit operations + */ +function transformObjectPattern(patternNode: SgNode, edits: Edit[]): void { + // Collect existing identifiers to detect conflicts + const identifiers = patternNode + .findAll({ + rule: { kind: "shorthand_property_identifier_pattern" }, + }) + .map((n) => n.text()); + + // Handle shorthand SlowBuffer references: { SlowBuffer } + for (const node of patternNode.findAll({ + rule: { kind: "shorthand_property_identifier_pattern", regex: "^SlowBuffer$" }, + })) { + if (identifiers.includes("Buffer")) { + // Buffer already exists, remove SlowBuffer using pure AST manipulation + removeSlowBufferFromObjectPattern(patternNode, edits); + } else { + // No conflict, just replace SlowBuffer with Buffer + edits.push(node.replace("Buffer")); + } + } + + // Handle aliased patterns: { SlowBuffer: alias } or { SlowBuffer as alias } + for (const node of patternNode.findAll({ + rule: { kind: "property_identifier", regex: "^SlowBuffer$" }, + })) { + // Replace the property key SlowBuffer with Buffer + edits.push(node.replace("Buffer")); + } + + // Handle ESM-style aliased patterns: { SlowBuffer as alias } + // These are handled as import_specifier in ESM contexts, but we check here too for completeness + for (const node of patternNode.findAll({ + rule: { + kind: "import_specifier", + has: { + field: "name", + kind: "identifier", + regex: "^SlowBuffer$", + }, + }, + })) { + const nameField = node.field("name"); + if (nameField) { + edits.push(nameField.replace("Buffer")); + } + } +} + +/** + * Helper function to remove SlowBuffer from import statements using AST manipulation + * @param importClause - The import_clause node containing the SlowBuffer specifier + * @param edits - Array to collect edit operations + */ +function removeSlowBufferFromImport(importClause: SgNode, edits: Edit[]): void { + // Find all import specifiers + const allSpecifiers = importClause.findAll({ rule: { kind: "import_specifier" } }); + + // Filter out SlowBuffer specifiers + const remainingSpecifiers = allSpecifiers.filter((spec) => { + const imported = spec.child(0); + return imported && imported.text() !== "SlowBuffer"; + }); + + if (remainingSpecifiers.length === 0) { + // No remaining specifiers, this would result in empty import + // Should not happen in practice since we check for Buffer conflict + return; + } + + // Reconstruct the named imports with remaining specifiers + const namedImports = importClause.find({ rule: { kind: "named_imports" } }); + if (namedImports) { + const newSpecifiersText = remainingSpecifiers.map((spec) => spec.text()).join(", "); + edits.push(namedImports.replace(`{ ${newSpecifiersText} }`)); + } +} + +/** + * Helper function to remove SlowBuffer from object patterns using pure AST manipulation + * Handles patterns like { SlowBuffer, Buffer, other } → { Buffer, other } + * @param patternNode - The object_pattern node containing SlowBuffer + * @param edits - Array to collect edit operations + */ +function removeSlowBufferFromObjectPattern(patternNode: SgNode, edits: Edit[]): void { + // Find all shorthand property identifiers in the pattern + const allProperties = patternNode.findAll({ + rule: { kind: "shorthand_property_identifier_pattern" }, + }); + + // Filter out SlowBuffer properties + const remainingProperties = allProperties.filter((prop) => prop.text() !== "SlowBuffer"); + + // Also collect any other pattern types (like pair_pattern for aliases) + const otherPatterns = patternNode.findAll({ + rule: { kind: "pair_pattern" }, + }); + + // Combine all remaining elements + const allRemainingElements = [...remainingProperties, ...otherPatterns]; + + if (allRemainingElements.length === 0) { + // This would result in empty pattern {}, which shouldn't happen + // since we know Buffer exists + return; + } + + // Reconstruct the object pattern with remaining elements + const newPatternText = allRemainingElements.map((elem) => elem.text()).join(", "); + edits.push(patternNode.replace(`{ ${newPatternText} }`)); +} + +/** + * Transforms actual SlowBuffer usage (constructor calls and function calls) + * Uses resolveBindingPath to determine how SlowBuffer was imported, then transforms: + * - new SlowBuffer(100) → Buffer.allocUnsafeSlow(100) + * - new alias(100) → alias.allocUnsafeSlow(100) (when SlowBuffer imported as alias) + * - SlowBuffer(100) → Buffer.allocUnsafeSlow(100) + * @param rootNode - The AST root node to search for usage patterns + * @param edits - Array to collect edit operations + */ +function processSlowBufferUsage(rootNode: SgNode, edits: Edit[]): void { + const root = rootNode.getRoot(); + const importStatements = getNodeImportStatements(root, "buffer"); + const requireStatements = getNodeRequireCalls(root, "buffer"); + const dynamicImportStatements = getNodeImportCalls(root, "buffer"); + + for (const importNode of [ + ...importStatements, + ...requireStatements, + ...dynamicImportStatements, + ]) { + 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) { + try { + const argsMatches = match.getMultipleMatches("ARGS"); + const argsField = match.field("arguments"); + const originalArgsText = argsField ? argsField.text().slice(1, -1) : ""; + const argsText = + argsMatches.length > 0 ? argsMatches.map((a) => a.text()).join(", ") : originalArgsText; + + const replacement = + binding === "SlowBuffer" + ? `Buffer.allocUnsafeSlow(${argsText})` + : `${binding}.allocUnsafeSlow(${argsText})`; + + edits.push(match.replace(replacement)); + } catch { + const args = match.field("arguments"); + if (args) { + const argsText = args.text().slice(1, -1); + const replacement = + binding === "SlowBuffer" + ? `Buffer.allocUnsafeSlow(${argsText})` + : `${binding}.allocUnsafeSlow(${argsText})`; + edits.push(match.replace(replacement)); + } + } + } + } catch { + continue; + } + } + + const directSlowBufferCalls = rootNode.findAll({ + rule: { + any: [ + { kind: "new_expression", pattern: "new SlowBuffer($$$ARGS)" }, + { kind: "call_expression", pattern: "SlowBuffer($$$ARGS)" }, + ], + }, + }); + + for (const match of directSlowBufferCalls) { + try { + const argsMatches = match.getMultipleMatches("ARGS"); + const argsField = match.field("arguments"); + const originalArgsText = argsField ? argsField.text().slice(1, -1) : ""; + const argsText = + argsMatches.length > 0 ? argsMatches.map((a) => a.text()).join(", ") : originalArgsText; + + edits.push(match.replace(`Buffer.allocUnsafeSlow(${argsText})`)); + } catch { + const args = match.field("arguments"); + if (args) { + const argsText = args.text().slice(1, -1); + edits.push(match.replace(`Buffer.allocUnsafeSlow(${argsText})`)); + } + } + } +} 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