diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml index 74e867ace62077..c028204481d44c 100644 --- a/lib/.eslintrc.yaml +++ b/lib/.eslintrc.yaml @@ -243,6 +243,9 @@ rules: into: Safe - name: String - name: Symbol + polyfilled: + - asyncDispose + - dispose - name: SyntaxError - name: TypeError - name: Uint16Array diff --git a/test/parallel/test-eslint-prefer-primordials.js b/test/parallel/test-eslint-prefer-primordials.js index ba44e88cf9c4ec..54eecd2c4e2e49 100644 --- a/test/parallel/test-eslint-prefer-primordials.js +++ b/test/parallel/test-eslint-prefer-primordials.js @@ -176,6 +176,22 @@ new RuleTester({ options: [{ name: 'Symbol' }], errors: [{ message: /const { SymbolIterator } = primordials/ }] }, + { + code: ` + const { SymbolAsyncDispose } = primordials; + const a = { [SymbolAsyncDispose] () {} } + `, + options: [{ name: 'Symbol', polyfilled: ['asyncDispose', 'dispose'] }], + errors: [{ message: /const { SymbolAsyncDispose } = require\("internal\/util"\)/ }] + }, + { + code: ` + const { SymbolDispose } = primordials; + const a = { [SymbolDispose] () {} } + `, + options: [{ name: 'Symbol', polyfilled: ['asyncDispose', 'dispose'] }], + errors: [{ message: /const { SymbolDispose } = require\("internal\/util"\)/ }] + }, { code: ` const { ObjectDefineProperty, Symbol } = primordials; diff --git a/tools/eslint-rules/prefer-primordials.js b/tools/eslint-rules/prefer-primordials.js index 48b351b46ae270..4f109748716cd6 100644 --- a/tools/eslint-rules/prefer-primordials.js +++ b/tools/eslint-rules/prefer-primordials.js @@ -74,6 +74,7 @@ module.exports = { meta: { messages: { error: 'Use `const { {{name}} } = primordials;` instead of the global.', + errorPolyfill: 'Use `const { {{name}} } = require("internal/util");` instead of the primordial.', }, schema: { type: 'array', @@ -88,6 +89,10 @@ module.exports = { items: { type: 'string' }, }, into: { type: 'string' }, + polyfilled: { + type: 'array', + items: { type: 'string' }, + }, }, additionalProperties: false, }, @@ -99,6 +104,7 @@ module.exports = { const nameMap = new Map(); const renameMap = new Map(); + const polyfilledSet = new Set(); for (const option of context.options) { const names = option.ignore || []; @@ -109,6 +115,11 @@ module.exports = { if (option.into) { renameMap.set(option.name, option.into); } + if (option.polyfilled) { + for (const propertyName of option.polyfilled) { + polyfilledSet.add(`${option.name}${propertyName[0].toUpperCase()}${propertyName.slice(1)}`); + } + } } let reported; @@ -186,6 +197,17 @@ module.exports = { }, VariableDeclarator(node) { const name = node.init?.name; + if (name === 'primordials' && node.id.type === 'ObjectPattern') { + const name = node.id.properties.find(({ key }) => polyfilledSet.has(key.name))?.key.name; + if (name) { + context.report({ + node, + messageId: 'errorPolyfill', + data: { name }, + }); + return; + } + } if (name !== undefined && isTarget(nameMap, name) && node.id.type === 'Identifier' && !globalScope.set.get(name)?.defs.length) {