-
-
Notifications
You must be signed in to change notification settings - Fork 20
feat(bytesRead-to-bytesWritten): implement Node.js DEP0108 migration #224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
af2bd57
806f4c3
62ec425
5c34494
5e2c8cf
e92bac6
847e000
7e316ca
0909317
ef52444
1cfa52d
bb1223d
e9df49a
a209d7e
6e18827
147e783
5be0912
6d14a43
e626aab
89dea38
88717b5
0b25d1e
a20437b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
# `zlib.bytesRead` → `zlib.bytesWritten` DEP0108 | ||
|
||
This codemod replace zlib.bytesRead with zlib.bytesWritten for consistent stream property naming. It's useful to migrate code that uses the deprecated property which has been removed. | ||
|
||
It replace zlib.bytesRead with zlib.bytesWritten in all zlib transform streams and it handle both CommonJS and ESM imports. | ||
AugustinMauroy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
See [DEP0108](https://nodejs.org/api/deprecations.html#DEP0108). | ||
|
||
--- | ||
|
||
## Example | ||
|
||
**Case 1** | ||
|
||
Before: | ||
|
||
```js | ||
const zlib = require("node:zlib"); | ||
const gzip = zlib.createGzip(); | ||
gzip.on("end", () => { | ||
console.log("Bytes processed:", gzip.bytesRead); | ||
}); | ||
``` | ||
|
||
After: | ||
|
||
```js | ||
const zlib = require("node:zlib"); | ||
const gzip = zlib.createGzip(); | ||
gzip.on("end", () => { | ||
console.log("Bytes processed:", gzip.bytesWritten); | ||
}); | ||
``` | ||
Comment on lines
+15
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @alexbit-codemod what was the verdict about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @JakobJingleheimer FYI I asked for that on codemod slack There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PR to support diff view is up... will go live by tomorrow feel free to use that moving forward.... @JakobJingleheimer , @AugustinMauroy There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. w00t! thanks 😁 |
||
|
||
**Case 2** | ||
|
||
Before: | ||
|
||
```js | ||
const zlib = require("node:zlib"); | ||
const deflate = zlib.createDeflate(); | ||
deflate.on("finish", () => { | ||
const stats = { | ||
input: deflate.bytesRead, | ||
output: deflate.bytesWritten | ||
}; | ||
}); | ||
``` | ||
|
||
After: | ||
|
||
```js | ||
const zlib = require("node:zlib"); | ||
const deflate = zlib.createDeflate(); | ||
deflate.on("finish", () => { | ||
const stats = { | ||
input: deflate.bytesWritten, | ||
output: deflate.bytesWritten | ||
}; | ||
}); | ||
``` | ||
|
||
**Case 3** | ||
|
||
Before: | ||
|
||
```js | ||
const zlib = require("node:zlib"); | ||
function trackProgress(stream) { | ||
setInterval(() => { | ||
console.log(`Progress: ${stream.bytesRead} bytes`); | ||
}, 1000); | ||
} | ||
``` | ||
|
||
After: | ||
|
||
```js | ||
const zlib = require("node:zlib"); | ||
function trackProgress(stream) { | ||
setInterval(() => { | ||
console.log(`Progress: ${stream.bytesWritten} bytes`); | ||
}, 1000); | ||
} | ||
``` | ||
|
||
**Case 4** | ||
|
||
Before: | ||
|
||
```js | ||
import { createGzip } from "node:zlib"; | ||
const gzip = createGzip(); | ||
const bytesProcessed = gzip.bytesRead; | ||
``` | ||
|
||
After: | ||
|
||
```js | ||
import { createGzip } from "node:zlib"; | ||
const gzip = createGzip(); | ||
const bytesProcessed = gzip.bytesWritten; | ||
``` | ||
|
||
**Case 5** | ||
|
||
Before: | ||
|
||
```js | ||
const zlib = require("node:zlib"); | ||
const gzip = zlib.createGzip(); | ||
const processed = gzip.bytesRead; | ||
``` | ||
|
||
After: | ||
|
||
```js | ||
const zlib = require("node:zlib"); | ||
const gzip = zlib.createGzip(); | ||
const processed = gzip.bytesWritten; | ||
``` | ||
|
||
**Case 6** | ||
|
||
Before: | ||
|
||
```js | ||
const { createGzip } = require("node:zlib"); | ||
const gzip = createGzip(); | ||
const bytes = gzip.bytesRead; | ||
``` | ||
|
||
After: | ||
|
||
```js | ||
const { createGzip } = require("node:zlib"); | ||
const gzip = createGzip(); | ||
const bytes = gzip.bytesWritten; | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
schema_version: "1.0" | ||
name: "@nodejs/zlib-bytesread-to-byteswritten" | ||
version: 1.0.0 | ||
description: Handle DEP0108 by replacing deprecated `zlib.bytesRead` with `zlib.bytesWritten` in Node.js transform streams | ||
author: Elie Khoury | ||
license: MIT | ||
workflow: workflow.yaml | ||
category: migration | ||
|
||
targets: | ||
languages: | ||
- javascript | ||
- typescript | ||
|
||
keywords: | ||
- transformation | ||
- migration | ||
- zlib | ||
- bytesRead | ||
- bytesWritten | ||
|
||
registry: | ||
access: public | ||
visibility: public |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"name": "@nodejs/zlib-bytesread-to-byteswritten", | ||
"version": "1.0.0", | ||
"description": "Replace deprecated `zlib.bytesRead` with `zlib.bytesWritten` in Node.js transform streams", | ||
"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/zlib-bytesread-to-byteswritten", | ||
"bugs": "https://github.com/nodejs/userland-migrations/issues" | ||
}, | ||
"author": "Elie Khoury", | ||
"license": "MIT", | ||
"homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/zlib-bytesread-to-byteswritten/README.md", | ||
"devDependencies": { | ||
"@codemod.com/jssg-types": "^1.0.9" | ||
}, | ||
"dependencies": { | ||
"@nodejs/codemod-utils": "*" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { getNodeRequireCalls } from "@nodejs/codemod-utils/ast-grep/require-call"; | ||
import { getNodeImportStatements } from "@nodejs/codemod-utils/ast-grep/import-statement"; | ||
import { resolveBindingPath } from "@nodejs/codemod-utils/ast-grep/resolve-binding-path"; | ||
import { removeLines } from "@nodejs/codemod-utils/ast-grep/remove-lines"; | ||
import type { Edit, Range, SgRoot } from "@codemod.com/jssg-types/main"; | ||
import type Js from "@codemod.com/jssg-types/langs/javascript"; | ||
|
||
const ZLIB_FACTORIES = [ | ||
"createGzip", | ||
"createGunzip", | ||
"createDeflate", | ||
"createInflate", | ||
"createBrotliCompress", | ||
"createBrotliDecompress", | ||
"createUnzip", | ||
]; | ||
|
||
export default function transform(root: SgRoot<Js>): string | null { | ||
const rootNode = root.root(); | ||
const edits: Edit[] = []; | ||
const linesToRemove: Range[] = []; | ||
|
||
// 1 Find all static zlib imports/requires | ||
const importNodes = [ | ||
...getNodeRequireCalls(root, "node:zlib"), | ||
...getNodeImportStatements(root, "node:zlib") | ||
]; | ||
|
||
const factoryBindings: string[] = []; | ||
const streamVariables: string[] = []; | ||
|
||
for (const node of importNodes) { | ||
const baseBind = resolveBindingPath(node, "$"); | ||
if (baseBind) { | ||
Eliekhoury17 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
for (const factory of ZLIB_FACTORIES) factoryBindings.push(`${baseBind}.${factory}`); | ||
} | ||
for (const factory of ZLIB_FACTORIES) { | ||
const binding = resolveBindingPath(node, `$.${factory}`); | ||
if (binding && !factoryBindings.includes(binding)) factoryBindings.push(binding); | ||
Eliekhoury17 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
// 1.b Handle dynamic imports: `await import("node:zlib")` | ||
const dynamicImports = rootNode.findAll({ rule: { pattern: "const $$$VAR = await import($$$MODULE)" } }); | ||
Eliekhoury17 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
for (const imp of dynamicImports) { | ||
const moduleName = imp.getMultipleMatches("MODULE")[0]?.text().replace(/['"]/g, ""); | ||
Eliekhoury17 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
if (moduleName === "node:zlib") { | ||
const varName = imp.getMultipleMatches("VAR")[0]?.text(); | ||
Eliekhoury17 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
if (varName) { | ||
for (const factory of ZLIB_FACTORIES) { | ||
factoryBindings.push(`${varName}.${factory}`); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (!importNodes.length && dynamicImports.length === 0) return null; | ||
|
||
// 2 Track variables assigned from factories (const, let, var) | ||
for (const factory of factoryBindings) { | ||
const patterns = [ | ||
`const $$$VAR = ${factory}($$$ARGS)`, | ||
`let $$$VAR = ${factory}($$$ARGS)`, | ||
`var $$$VAR = ${factory}($$$ARGS)` | ||
]; | ||
Eliekhoury17 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
for (const pattern of patterns) { | ||
const matches = rootNode.findAll({ rule: { pattern } }); | ||
for (const match of matches) { | ||
const varMatch = match.getMultipleMatches("VAR"); | ||
if (varMatch.length) { | ||
Eliekhoury17 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
const varName = varMatch[0].text(); | ||
if (!streamVariables.includes(varName)) streamVariables.push(varName); | ||
} | ||
} | ||
} | ||
} | ||
|
||
// 3 Replace .bytesRead → .bytesWritten for tracked variables | ||
for (const variable of streamVariables) { | ||
const matches = rootNode.findAll({ rule: { pattern: `${variable}.bytesRead` } }); | ||
for (const match of matches) { | ||
Eliekhoury17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
edits.push(match.replace(match.text().replace(".bytesRead", ".bytesWritten"))); | ||
} | ||
} | ||
|
||
// 4 Replace .bytesRead → .bytesWritten for function parameters | ||
const funcPatterns = ["function $$$NAME($$$PARAMS) { $$$BODY }"]; | ||
for (const pattern of funcPatterns) { | ||
const funcs = rootNode.findAll({ rule: { pattern } }); | ||
Eliekhoury17 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
for (const func of funcs) { | ||
Eliekhoury17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const params = func.getMultipleMatches("PARAMS"); | ||
for (const param of params) { | ||
Eliekhoury17 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
const paramNames = param | ||
.text() | ||
.split(",") | ||
.map((p) => p.replace(/\/\*.*\*\//, "").trim()) | ||
Eliekhoury17 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
.filter(Boolean); | ||
|
||
for (const paramName of paramNames) { | ||
const matches = rootNode.findAll({ rule: { pattern: `${paramName}.bytesRead` } }); | ||
for (const match of matches) { | ||
Eliekhoury17 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
edits.push(match.replace(match.text().replace(".bytesRead", ".bytesWritten"))); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (!edits.length) return null; | ||
return removeLines(rootNode.commitEdits(edits), linesToRemove); | ||
Eliekhoury17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
const zlib = require("node:zlib"); | ||
const gzip = zlib.createGzip(); | ||
const processed = gzip.bytesWritten; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
const zlib = require("node:zlib"); | ||
const deflate = zlib.createDeflate(); | ||
deflate.on("finish", () => { | ||
const stats = { | ||
input: deflate.bytesWritten, | ||
output: deflate.bytesWritten | ||
}; | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
const { createGzip } = require("node:zlib"); | ||
const gzip = createGzip(); | ||
const bytes = gzip.bytesWritten; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// CommonJS style dynamic import | ||
async function testCommonJS() { | ||
const zlib = await import("node:zlib"); | ||
const gzip = zlib.createGzip(); | ||
console.log(gzip.bytesWritten); | ||
} | ||
|
||
// ESM style dynamic import | ||
const zlib = await import("node:zlib"); | ||
const gzip = zlib.createGzip(); | ||
console.log(gzip.bytesWritten); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { createGzip } from "node:zlib"; | ||
const gzip = createGzip(); | ||
const bytesProcessed = gzip.bytesWritten; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const zlib = require("node:zlib"); | ||
const gzip = zlib.createGzip(); | ||
gzip.on("end", () => { | ||
console.log("Bytes processed:", gzip.bytesWritten); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
const zlib = require("node:zlib"); | ||
|
||
// using let | ||
let deflateStream = zlib.createDeflate(); | ||
console.log(deflateStream.bytesWritten); | ||
|
||
// using var | ||
var gzipStream = zlib.createGzip(); | ||
console.log(gzipStream.bytesWritten); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
const zlib = require("node:zlib"); | ||
function trackProgress(stream) { | ||
setInterval(() => { | ||
console.log(`Progress: ${stream.bytesWritten} bytes`); | ||
}, 1000); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
const zlib = require("node:zlib"); | ||
const gzip = zlib.createGzip(); | ||
const processed = gzip.bytesRead; |
Uh oh!
There was an error while loading. Please reload this page.