Skip to content

Commit 2c2fee2

Browse files
committed
feat(migrate-legacy-buffer-atob-btoa): introduce
1 parent 0a41d52 commit 2c2fee2

File tree

9 files changed

+173
-0
lines changed

9 files changed

+173
-0
lines changed

recipes/buffer-atob-btoa/README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# migrate legacy `buffer.atob()` and `buffer.btoa()` APIs
2+
3+
Migrates usage of the legacy APIs `buffer.atob()` and `buffer.btoa()` to the current recommended approaches.
4+
5+
## Example
6+
7+
### Migrating buffer.atob(data)
8+
9+
**Before:**
10+
```
11+
const buffer = require('node:buffer');
12+
const data = 'SGVsbG8gV29ybGQh'; // "Hello World!" in base64
13+
const decodedData = buffer.atob(data);
14+
console.log(decodedData); // Outputs: Hello World!
15+
```
16+
17+
**After:**
18+
```
19+
const data = 'SGVsbG8gV29ybGQh'; // "Hello World!" in base64
20+
const decodedData = Buffer.from(data, 'base64').toString('binary');
21+
console.log(decodedData); // Outputs: Hello World!
22+
```
23+
24+
### Migrating buffer.btoa(data)
25+
26+
**Before:**
27+
```
28+
const buffer = require('node:buffer');
29+
const data = 'Hello World!';
30+
const encodedData = buffer.btoa(data);
31+
console.log(encodedData); // Outputs: SGVsbG8gV29ybGQh
32+
```
33+
34+
**After:**
35+
```
36+
const data = 'Hello World!';
37+
const encodedData = Buffer.from(data, 'binary').toString('base64');
38+
console.log(encodedData); // Outputs: SGVsbG8gV29ybGQh
39+
```
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
schema_version: "1.0"
2+
name: "@nodejs/buffer-atob-btoa"
3+
version: "1.0.0"
4+
description: Migrates usage of the legacy APIs `buffer.atob()` and `buffer.btoa()` to the current recommended approaches
5+
author: nekojanai (Jana)
6+
license: MIT
7+
workflow: workflow.yaml
8+
category: migration
9+
10+
targets:
11+
languages:
12+
- javascript
13+
- typescript
14+
15+
keywords:
16+
- transformation
17+
- migration
18+
19+
registry:
20+
access: public
21+
visibility: public
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "@nodejs/buffer-atob-btoa",
3+
"version": "1.0.0",
4+
"description": "Migrates usage of the legacy APIs `buffer.atob()` and `buffer.btoa()` to the current recommended approaches",
5+
"type": "module",
6+
"scripts": {
7+
"test": "npx codemod jssg test -l typescript ./src/workflow.ts ./"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/nodejs/userland-migrations.git",
12+
"directory": "recipes/buffer-atob-btoa",
13+
"bugs": "https://github.com/nodejs/userland-migrations/issues"
14+
},
15+
"author": "nekojanai (Jana)",
16+
"license": "MIT",
17+
"homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/buffer-atob-btoa/README.md",
18+
"dependencies": {
19+
"@nodejs/codemod-utils": "*"
20+
}
21+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type { Edit, Range, SgRoot } from '@codemod.com/jssg-types/main';
2+
import type Js from '@codemod.com/jssg-types/langs/javascript';
3+
import { getNodeRequireCalls } from '@nodejs/codemod-utils/ast-grep/require-call';
4+
import { removeLines } from '@nodejs/codemod-utils/ast-grep/remove-lines';
5+
6+
export default function transform(root: SgRoot<Js>): string | null {
7+
const rootNode = root.root()
8+
const edits: Edit[] = [];
9+
const linesToRemove: Range[] = [];
10+
11+
// @ts-expect-error - ast-grep types are not fully compatible with JSSG types
12+
const requireStatements = getNodeRequireCalls(root, 'buffer');
13+
const atobFunctionCalls = rootNode.findAll({
14+
rule: {
15+
pattern: `buffer.atob($ARG)`
16+
}
17+
});
18+
19+
// Remove all buffer require statements
20+
for (const statement of requireStatements) {
21+
linesToRemove.push(statement.range());
22+
}
23+
24+
// Rewrite atob function calls
25+
for (const call of atobFunctionCalls) {
26+
const argMatch = call.getMatch("ARG");
27+
if (argMatch) {
28+
const arg = argMatch.text();
29+
const replacement = `Buffer.from(${arg}, 'base64').toString('binary')`;
30+
edits.push(call.replace(replacement));
31+
}
32+
}
33+
34+
const btoaFunctionCalls = rootNode.findAll({
35+
rule: {
36+
pattern: `buffer.btoa($ARG)`
37+
}
38+
});
39+
40+
// Rewrite btoa function calls
41+
for (const call of btoaFunctionCalls) {
42+
const argMatch = call.getMatch("ARG");
43+
if (argMatch) {
44+
const arg = argMatch.text();
45+
const replacement = `Buffer.from(${arg}, 'binary').toString('base64')`;
46+
edits.push(call.replace(replacement));
47+
}
48+
}
49+
50+
return removeLines(rootNode.commitEdits(edits), linesToRemove);
51+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const data = 'SGVsbG8gV29ybGQh'; // "Hello World!" in base64
2+
const decodedData = Buffer.from(data, 'base64').toString('binary');
3+
console.log(decodedData); // Outputs: Hello World!
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const data = 'Hello World!';
2+
const encodedData = Buffer.from(data, 'binary').toString('base64');
3+
console.log(encodedData); // Outputs: SGVsbG8gV29ybGQh
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const buffer = require('node:buffer');
2+
const data = 'SGVsbG8gV29ybGQh'; // "Hello World!" in base64
3+
const decodedData = buffer.atob(data);
4+
console.log(decodedData); // Outputs: Hello World!
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const buffer = require('node:buffer');
2+
const data = 'Hello World!';
3+
const encodedData = buffer.btoa(data);
4+
console.log(encodedData); // Outputs: SGVsbG8gV29ybGQh
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/codemod-com/codemod/refs/heads/main/schemas/workflow.json
2+
3+
version: "1"
4+
5+
nodes:
6+
- id: apply-transforms
7+
name: Apply AST Transformations
8+
type: automatic
9+
runtime:
10+
type: direct
11+
steps:
12+
- name: Migrates usage of the legacy APIs `buffer.atob()` and `buffer.btoa()` to the current recommended approaches
13+
js-ast-grep:
14+
js_file: src/workflow.ts
15+
base_path: .
16+
include:
17+
- "**/*.cjs"
18+
- "**/*.js"
19+
- "**/*.jsx"
20+
- "**/*.mjs"
21+
- "**/*.cts"
22+
- "**/*.mts"
23+
- "**/*.ts"
24+
- "**/*.tsx"
25+
exclude:
26+
- "**/node_modules/**"
27+
language: typescript

0 commit comments

Comments
 (0)