From 94ffcdbf9cb6ff15d6e541f386da4b8ee2630433 Mon Sep 17 00:00:00 2001 From: LiviaMedeiros Date: Mon, 12 May 2025 23:07:36 +0800 Subject: [PATCH 1/2] tools: support explicit resource management in eslint --- eslint.config.mjs | 3 +++ tools/eslint/package-lock.json | 16 ++++++++++++++++ tools/eslint/package.json | 1 + 3 files changed, 20 insertions(+) diff --git a/eslint.config.mjs b/eslint.config.mjs index 777524660dddb0..75241d0eed65c4 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -18,6 +18,8 @@ import nodeCore from './tools/eslint/eslint-plugin-node-core.js'; const { globalIgnores } = await importEslintTool('eslint/config'); const { default: js } = await importEslintTool('@eslint/js'); const { default: babelEslintParser } = await importEslintTool('@babel/eslint-parser'); +const babelPluginProposalExplicitResourceManagement = + resolveEslintTool('@babel/plugin-proposal-explicit-resource-management'); const babelPluginSyntaxImportAttributes = resolveEslintTool('@babel/plugin-syntax-import-attributes'); const babelPluginSyntaxImportSource = resolveEslintTool('@babel/plugin-syntax-import-source'); const { default: jsdoc } = await importEslintTool('eslint-plugin-jsdoc'); @@ -103,6 +105,7 @@ export default [ parserOptions: { babelOptions: { plugins: [ + babelPluginProposalExplicitResourceManagement, babelPluginSyntaxImportAttributes, babelPluginSyntaxImportSource, ], diff --git a/tools/eslint/package-lock.json b/tools/eslint/package-lock.json index 2447e83cf532c6..ab8b874cf3e9d0 100644 --- a/tools/eslint/package-lock.json +++ b/tools/eslint/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@babel/core": "^7.27.1", "@babel/eslint-parser": "^7.27.1", + "@babel/plugin-proposal-explicit-resource-management": "^7.27.1", "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-import-source": "^7.27.1", "@stylistic/eslint-plugin-js": "^4.2.0", @@ -230,6 +231,21 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-proposal-explicit-resource-management": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-explicit-resource-management/-/plugin-proposal-explicit-resource-management-7.27.1.tgz", + "integrity": "sha512-XYOgHCv2IO+OtCjttDzuXLgHQEaPq8tdDGdeXRNRK4XobeXSF/SuMKndvdgvzQbMzKGQ1ANkjFICA5BS55rCMg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", diff --git a/tools/eslint/package.json b/tools/eslint/package.json index d0fbd7d433cc8a..9909ded4d33533 100644 --- a/tools/eslint/package.json +++ b/tools/eslint/package.json @@ -7,6 +7,7 @@ "@babel/eslint-parser": "^7.27.1", "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-import-source": "^7.27.1", + "@babel/plugin-proposal-explicit-resource-management": "^7.27.1", "@stylistic/eslint-plugin-js": "^4.2.0", "eslint": "^9.25.1", "eslint-formatter-tap": "^8.40.0", From 2b63b3d9f808589d4821ab31d8d13a07634cce55 Mon Sep 17 00:00:00 2001 From: LiviaMedeiros Date: Mon, 12 May 2025 22:27:33 +0800 Subject: [PATCH 2/2] stream: test explicit resource management implicitly --- test/parallel/test-stream-duplex-destroy.js | 14 ++++++++++++++ test/parallel/test-stream-readable-dispose.js | 15 +++++++++++++++ test/parallel/test-stream-transform-destroy.js | 12 ++++++++++++ test/parallel/test-stream-writable-destroy.js | 14 ++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/test/parallel/test-stream-duplex-destroy.js b/test/parallel/test-stream-duplex-destroy.js index 52867384057f59..5c9e1882095c9a 100644 --- a/test/parallel/test-stream-duplex-destroy.js +++ b/test/parallel/test-stream-duplex-destroy.js @@ -284,3 +284,17 @@ const assert = require('assert'); duplex.on('close', common.mustCall()); duplex[Symbol.asyncDispose]().then(common.mustCall()); } + +(async () => { + // Check Symbol.asyncDispose implicitly + await using duplex = new Duplex({ + write(chunk, enc, cb) { cb(); }, + read() {}, + }); + duplex.on('error', common.mustCall(function(e) { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(this.destroyed, true); + assert.strictEqual(this.errored.name, 'AbortError'); + })); + duplex.on('close', common.mustCall()); +})().then(common.mustCall()); diff --git a/test/parallel/test-stream-readable-dispose.js b/test/parallel/test-stream-readable-dispose.js index e940bf1688f2bf..118c83b1fb2543 100644 --- a/test/parallel/test-stream-readable-dispose.js +++ b/test/parallel/test-stream-readable-dispose.js @@ -21,3 +21,18 @@ const assert = require('assert'); assert.strictEqual(read.destroyed, true); })); } + +(async () => { + await using read = new Readable({ + read() {} + }); + read.resume(); + + read.on('end', common.mustNotCall('no end event')); + read.on('close', common.mustCall()); + read.on('error', common.mustCall(function(err) { + assert.strictEqual(err.name, 'AbortError'); + assert.strictEqual(this.errored.name, 'AbortError'); + assert.strictEqual(this.destroyed, true); + })); +})().then(common.mustCall()); diff --git a/test/parallel/test-stream-transform-destroy.js b/test/parallel/test-stream-transform-destroy.js index 428bab9ce33fcf..e4410e34c26fc4 100644 --- a/test/parallel/test-stream-transform-destroy.js +++ b/test/parallel/test-stream-transform-destroy.js @@ -152,3 +152,15 @@ const assert = require('assert'); transform.on('close', common.mustCall()); transform[Symbol.asyncDispose]().then(common.mustCall()); } + +(async () => { + await using transform = new Transform({ + transform(chunk, enc, cb) {} + }); + transform.on('error', common.mustCall(function(err) { + assert.strictEqual(err.name, 'AbortError'); + assert.strictEqual(this.destroyed, true); + assert.strictEqual(this.errored.name, 'AbortError'); + })); + transform.on('close', common.mustCall()); +})().then(common.mustCall()); diff --git a/test/parallel/test-stream-writable-destroy.js b/test/parallel/test-stream-writable-destroy.js index 05d7932b88c182..99981a20345fdc 100644 --- a/test/parallel/test-stream-writable-destroy.js +++ b/test/parallel/test-stream-writable-destroy.js @@ -499,3 +499,17 @@ const assert = require('assert'); })); write[Symbol.asyncDispose]().then(common.mustCall()); } + +(async () => { + await using write = new Writable({ + write(chunk, enc, cb) { cb(); } + }); + + write.on('error', common.mustCall(function(e) { + assert.strictEqual(e.name, 'AbortError'); + assert.strictEqual(this.destroyed, true); + assert.strictEqual(this.errored.name, 'AbortError'); + })); + write.on('close', common.mustCall()); + write.on('finish', common.mustNotCall('no finish event')); +})().then(common.mustCall());