From f34d8de65af034463358cb6144f35308e9b841d9 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Wed, 6 Jan 2021 19:53:27 +0100 Subject: [PATCH 1/2] lib: remove use of array destructuring PR-URL: https://github.com/nodejs/node/pull/36818 Reviewed-By: Rich Trott --- lib/_http_server.js | 2 +- lib/assert.js | 4 ++-- lib/events.js | 2 +- lib/fs.js | 3 ++- lib/internal/bootstrap/loaders.js | 2 +- lib/internal/cluster/primary.js | 3 ++- lib/internal/cluster/round_robin_handle.js | 5 +++-- lib/internal/console/constructor.js | 2 +- lib/internal/crypto/keys.js | 12 ++++++------ lib/internal/errors.js | 7 ++++--- lib/internal/main/print_help.js | 8 ++++---- lib/internal/modules/cjs/loader.js | 2 +- lib/internal/policy/manifest.js | 5 +++-- lib/internal/util/comparisons.js | 4 ++-- lib/internal/util/inspect.js | 6 +++--- lib/internal/worker/js_transferable.js | 2 +- lib/os.js | 10 +++++----- lib/repl.js | 6 +++--- 18 files changed, 45 insertions(+), 40 deletions(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index 23a6906dab5004..dac5fedf433533 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -408,7 +408,7 @@ Server.prototype.setTimeout = function setTimeout(msecs, callback) { Server.prototype[EE.captureRejectionSymbol] = function(err, event, ...args) { switch (event) { case 'request': - const [ , res] = args; + const { 1: res } = args; if (!res.headersSent && !res.writableEnded) { // Don't leak headers. ArrayPrototypeForEach(res.getHeaderNames(), diff --git a/lib/assert.js b/lib/assert.js index b13aaa93e2ccf9..8ee15fc81ef13d 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -335,7 +335,7 @@ function getErrMessage(message, fn) { } fd = openSync(filename, 'r', 0o666); // Reset column and message. - [column, message] = getCode(fd, line, column); + ({ 0: column, 1: message } = getCode(fd, line, column)); // Flush unfinished multi byte characters. decoder.end(); } else { @@ -343,7 +343,7 @@ function getErrMessage(message, fn) { code = StringPrototypeSlice(code, StringPrototypeIndexOf(code, '\n') + 1); } - [column, message] = parseCode(code, column); + ({ 0: column, 1: message } = parseCode(code, column)); } // Always normalize indentation, otherwise the message could look weird. if (StringPrototypeIncludes(message, '\n')) { diff --git a/lib/events.js b/lib/events.js index 797b5bc90c6bb8..501c05f3a4a5dd 100644 --- a/lib/events.js +++ b/lib/events.js @@ -315,7 +315,7 @@ function enhanceStackTrace(err, own) { const errStack = err.stack.split('\n').slice(1); const ownStack = own.stack.split('\n').slice(1); - const [ len, off ] = identicalSequenceRange(ownStack, errStack); + const { 0: len, 1: off } = identicalSequenceRange(ownStack, errStack); if (len > 0) { ownStack.splice(off + 1, len - 2, ' [... lines matching original stack trace ...]'); diff --git a/lib/fs.js b/lib/fs.js index 890388d251431e..7a048088813753 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -2074,7 +2074,8 @@ function copyFileSync(src, dest, mode) { function lazyLoadStreams() { if (!ReadStream) { ({ ReadStream, WriteStream } = require('internal/fs/streams')); - [ FileReadStream, FileWriteStream ] = [ ReadStream, WriteStream ]; + FileReadStream = ReadStream; + FileWriteStream = WriteStream; } } diff --git a/lib/internal/bootstrap/loaders.js b/lib/internal/bootstrap/loaders.js index 8f1e2996416814..40b54fa9c63e5a 100644 --- a/lib/internal/bootstrap/loaders.js +++ b/lib/internal/bootstrap/loaders.js @@ -198,7 +198,7 @@ class NativeModule { // To be called during pre-execution when --expose-internals is on. // Enables the user-land module loader to access internal modules. static exposeInternals() { - for (const [id, mod] of NativeModule.map) { + for (const { 0: id, 1: mod } of NativeModule.map) { // Do not expose this to user land even with --expose-internals. if (id !== loaderId) { mod.canBeRequiredByUsers = true; diff --git a/lib/internal/cluster/primary.js b/lib/internal/cluster/primary.js index e8113411790a11..b4644748c0af16 100644 --- a/lib/internal/cluster/primary.js +++ b/lib/internal/cluster/primary.js @@ -23,7 +23,8 @@ const cluster = new EventEmitter(); const intercom = new EventEmitter(); const SCHED_NONE = 1; const SCHED_RR = 2; -const [ minPort, maxPort ] = [ 1024, 65535 ]; +const minPort = 1024; +const maxPort = 65535; const { validatePort } = require('internal/validators'); module.exports = cluster; diff --git a/lib/internal/cluster/round_robin_handle.js b/lib/internal/cluster/round_robin_handle.js index 5dc53ef78b1a6e..778976210165a2 100644 --- a/lib/internal/cluster/round_robin_handle.js +++ b/lib/internal/cluster/round_robin_handle.js @@ -93,10 +93,11 @@ RoundRobinHandle.prototype.remove = function(worker) { RoundRobinHandle.prototype.distribute = function(err, handle) { ArrayPrototypePush(this.handles, handle); - const [ workerEntry ] = this.free; + // eslint-disable-next-line node-core/no-array-destructuring + const [ workerEntry ] = this.free; // this.free is a SafeMap if (ArrayIsArray(workerEntry)) { - const [ workerId, worker ] = workerEntry; + const { 0: workerId, 1: worker } = workerEntry; this.free.delete(workerId); this.handoff(worker); } diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index 2677af577c01c2..c3716a8acda8b5 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -525,7 +525,7 @@ const consoleMethods = { length++; } } else { - for (const [k, v] of tabularData) { + for (const { 0: k, 1: v } of tabularData) { ArrayPrototypePush(keys, _inspect(k)); ArrayPrototypePush(values, _inspect(v)); length++; diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index a7bf4b8a65d8de..9b37cb3a43b8c8 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -86,12 +86,12 @@ for (const m of [[kKeyEncodingPKCS1, 'pkcs1'], [kKeyEncodingPKCS8, 'pkcs8'], // which requires the KeyObject base class to be implemented in C++. // The creation requires a callback to make sure that the NativeKeyObject // base class cannot exist without the other KeyObject implementations. -const [ - KeyObject, - SecretKeyObject, - PublicKeyObject, - PrivateKeyObject, -] = createNativeKeyObjectClass((NativeKeyObject) => { +const { + 0: KeyObject, + 1: SecretKeyObject, + 2: PublicKeyObject, + 3: PrivateKeyObject, +} = createNativeKeyObjectClass((NativeKeyObject) => { // Publicly visible KeyObject class. class KeyObject extends NativeKeyObject { constructor(type, handle) { diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 050193002a9559..194ea055a92e03 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -429,7 +429,7 @@ const captureLargerStackTrace = hideStackFrames( * @returns {Error} */ const uvException = hideStackFrames(function uvException(ctx) { - const [code, uvmsg] = uvErrmapGet(ctx.errno) || uvUnmappedError; + const { 0: code, 1: uvmsg } = uvErrmapGet(ctx.errno) || uvUnmappedError; let message = `${code}: ${ctx.message || uvmsg}, ${ctx.syscall}`; let path; @@ -485,7 +485,7 @@ const uvException = hideStackFrames(function uvException(ctx) { */ const uvExceptionWithHostPort = hideStackFrames( function uvExceptionWithHostPort(err, syscall, address, port) { - const [code, uvmsg] = uvErrmapGet(err) || uvUnmappedError; + const { 0: code, 1: uvmsg } = uvErrmapGet(err) || uvUnmappedError; const message = `${syscall} ${code}: ${uvmsg}`; let details = ''; @@ -1243,7 +1243,8 @@ E('ERR_MANIFEST_ASSERT_INTEGRITY', }" does not match the expected integrity.`; if (realIntegrities.size) { const sri = ArrayPrototypeJoin( - ArrayFrom(realIntegrities.entries(), ([alg, dgs]) => `${alg}-${dgs}`), + ArrayFrom(realIntegrities.entries(), + ({ 0: alg, 1: dgs }) => `${alg}-${dgs}`), ' ' ); msg += ` Integrities found are: ${sri}`; diff --git a/lib/internal/main/print_help.js b/lib/internal/main/print_help.js index 03294a9e378f4d..4855d959ef9057 100644 --- a/lib/internal/main/print_help.js +++ b/lib/internal/main/print_help.js @@ -110,9 +110,9 @@ function format( let text = ''; let maxFirstColumnUsed = 0; - for (const [ - name, { helpText, type, value }, - ] of ArrayPrototypeSort([...options.entries()])) { + for (const { + 0: name, 1: { helpText, type, value } + } of ArrayPrototypeSort([...options.entries()])) { if (!helpText) continue; let displayName = name; @@ -120,7 +120,7 @@ function format( if (argDescription) displayName += `=${argDescription}`; - for (const [ from, to ] of aliases) { + for (const { 0: from, 1: to } of aliases) { // For cases like e.g. `-e, --eval`. if (to[0] === name && to.length === 1) { displayName = `${from}, ${displayName}`; diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 3b97ddc342a325..837250198b7e63 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -175,7 +175,7 @@ function Module(id = '', parent) { } const builtinModules = []; -for (const [id, mod] of NativeModule.map) { +for (const { 0: id, 1: mod } of NativeModule.map) { if (mod.canBeRequiredByUsers) { ArrayPrototypePush(builtinModules, id); } diff --git a/lib/internal/policy/manifest.js b/lib/internal/policy/manifest.js index 6f4f094c980524..c3ec82f596a509 100644 --- a/lib/internal/policy/manifest.js +++ b/lib/internal/policy/manifest.js @@ -350,7 +350,8 @@ class Manifest { }; for (let i = 0; i < jsonResourcesEntries.length; i++) { - const [originalHREF, resourceDescriptor] = jsonResourcesEntries[i]; + const { 0: originalHREF, 1: resourceDescriptor } = + jsonResourcesEntries[i]; const cascade = resourceDescriptor.cascade; const dependencyMap = resourceDescriptor.dependencies; const resourceHREF = resolve(originalHREF); @@ -380,7 +381,7 @@ class Manifest { const scopeIntegrities = this.#scopeIntegrities; for (let i = 0; i < jsonScopesEntries.length; i++) { - const [originalHREF, scopeDescriptor] = jsonScopesEntries[i]; + const { 0: originalHREF, 1: scopeDescriptor } = jsonScopesEntries[i]; const integrity = scopeDescriptor.integrity; const cascade = scopeDescriptor.cascade; const dependencyMap = scopeDescriptor.dependencies; diff --git a/lib/internal/util/comparisons.js b/lib/internal/util/comparisons.js index ed8ddf6ad321d8..5efed17f3843f5 100644 --- a/lib/internal/util/comparisons.js +++ b/lib/internal/util/comparisons.js @@ -485,7 +485,7 @@ function mapHasEqualEntry(set, map, key1, item1, strict, memo) { function mapEquiv(a, b, strict, memo) { let set = null; - for (const [key, item1] of a) { + for (const { 0: key, 1: item1 } of a) { if (typeof key === 'object' && key !== null) { if (set === null) { set = new SafeSet(); @@ -512,7 +512,7 @@ function mapEquiv(a, b, strict, memo) { } if (set !== null) { - for (const [key, item] of b) { + for (const { 0: key, 1: item } of b) { if (typeof key === 'object' && key !== null) { if (!mapHasEqualEntry(set, a, key, item, strict, memo)) return false; diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 757a9f372c8dec..59219821230b51 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -1552,7 +1552,7 @@ function formatSet(value, ctx, ignored, recurseTimes) { function formatMap(value, ctx, ignored, recurseTimes) { const output = []; ctx.indentationLvl += 2; - for (const [k, v] of value) { + for (const { 0: k, 1: v } of value) { output.push(`${formatValue(ctx, k, recurseTimes)} => ` + formatValue(ctx, v, recurseTimes)); } @@ -1636,7 +1636,7 @@ function formatWeakMap(ctx, value, recurseTimes) { } function formatIterator(braces, ctx, value, recurseTimes) { - const [entries, isKeyValue] = previewEntries(value, true); + const { 0: entries, 1: isKeyValue } = previewEntries(value, true); if (isKeyValue) { // Mark entry iterators as such. braces[0] = braces[0].replace(/ Iterator] {$/, ' Entries] {'); @@ -1648,7 +1648,7 @@ function formatIterator(braces, ctx, value, recurseTimes) { function formatPromise(ctx, value, recurseTimes) { let output; - const [state, result] = getPromiseDetails(value); + const { 0: state, 1: result } = getPromiseDetails(value); if (state === kPending) { output = [ctx.stylize('', 'special')]; } else { diff --git a/lib/internal/worker/js_transferable.js b/lib/internal/worker/js_transferable.js index 5b822ef8a0bcb1..ce95cf64e21987 100644 --- a/lib/internal/worker/js_transferable.js +++ b/lib/internal/worker/js_transferable.js @@ -19,7 +19,7 @@ function setup() { // from .postMessage() calls. The format of `deserializeInfo` is generally // 'module:Constructor', e.g. 'internal/fs/promises:FileHandle'. setDeserializerCreateObjectFunction((deserializeInfo) => { - const [ module, ctor ] = StringPrototypeSplit(deserializeInfo, ':'); + const { 0: module, 1: ctor } = StringPrototypeSplit(deserializeInfo, ':'); const Ctor = require(module)[ctor]; if (typeof Ctor !== 'function' || !(Ctor.prototype instanceof JSTransferable)) { diff --git a/lib/os.js b/lib/os.js index d720b65ce441bf..19f784fc2667b2 100644 --- a/lib/os.js +++ b/lib/os.js @@ -71,11 +71,11 @@ function getCheckedFunction(fn) { }); } -const [ - type, - version, - release, -] = _getOSInformation(); +const { + 0: type, + 1: version, + 2: release, +} = _getOSInformation(); const getHomeDirectory = getCheckedFunction(_getHomeDirectory); const getHostname = getCheckedFunction(_getHostname); diff --git a/lib/repl.js b/lib/repl.js index 2649dc337c9c8d..28367ce899af2b 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -382,10 +382,10 @@ function REPLServer(prompt, let entry; const tmpCompletionEnabled = self.isCompletionEnabled; while (entry = ArrayPrototypeShift(pausedBuffer)) { - const [type, payload, isCompletionEnabled] = entry; + const { 0: type, 1: payload, 2: isCompletionEnabled } = entry; switch (type) { case 'key': { - const [d, key] = payload; + const { 0: d, 1: key } = payload; self.isCompletionEnabled = isCompletionEnabled; self._ttyWrite(d, key); break; @@ -1500,7 +1500,7 @@ function complete(line, callback) { REPLServer.prototype.completeOnEditorMode = (callback) => (err, results) => { if (err) return callback(err); - const [completions, completeOn = ''] = results; + const { 0: completions, 1: completeOn = '' } = results; let result = ArrayPrototypeFilter(completions, Boolean); if (completeOn && result.length !== 0) { From 26288ff25ee59c2052a71d44378d4eb3bc616596 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Wed, 6 Jan 2021 19:19:36 +0100 Subject: [PATCH 2/2] tools: add ESLint rule no-array-destructuring Iterating over arrays should be avoided because it relies on user-mutable global methods (`Array.prototype[Symbol.iterator]` and `%ArrayIteratorPrototype%.next`), we should instead use other alternatives. This commit adds a rule that disallow array destructuring syntax in favor of object destructuring syntax. Note that you can ignore this rule if you are using the array destructuring syntax over a safe iterable, or actually want to iterate over a user-provided object. PR-URL: https://github.com/nodejs/node/pull/36818 Reviewed-By: Rich Trott --- lib/.eslintrc.yaml | 1 + .../test-eslint-no-array-destructuring.js | 141 ++++++++++++++++++ tools/eslint-rules/no-array-destructuring.js | 83 +++++++++++ 3 files changed, 225 insertions(+) create mode 100644 test/parallel/test-eslint-no-array-destructuring.js create mode 100644 tools/eslint-rules/no-array-destructuring.js diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml index 05142865e12802..2cc2660157191f 100644 --- a/lib/.eslintrc.yaml +++ b/lib/.eslintrc.yaml @@ -28,6 +28,7 @@ rules: # Custom rules in tools/eslint-rules node-core/lowercase-name-for-primitive: error node-core/non-ascii-character: error + node-core/no-array-destructuring: error node-core/prefer-primordials: - error - name: Array diff --git a/test/parallel/test-eslint-no-array-destructuring.js b/test/parallel/test-eslint-no-array-destructuring.js new file mode 100644 index 00000000000000..be59ee69309755 --- /dev/null +++ b/test/parallel/test-eslint-no-array-destructuring.js @@ -0,0 +1,141 @@ +'use strict'; + +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +common.skipIfEslintMissing(); + +const { RuleTester } = require('../../tools/node_modules/eslint'); +const rule = require('../../tools/eslint-rules/no-array-destructuring'); + +const USE_OBJ_DESTRUCTURING = + 'Use object destructuring instead of array destructuring.'; +const USE_ARRAY_METHODS = + 'Use primordials.ArrayPrototypeSlice to avoid unsafe array iteration.'; + +new RuleTester({ + parserOptions: { ecmaVersion: 2021 }, + env: { es6: true } +}) + .run('no-array-destructuring', rule, { + valid: [ + 'const first = [1, 2, 3][0];', + 'const {0:first} = [1, 2, 3];', + '({1:elem} = array);', + 'function name(param, { 0: key, 1: value },) {}', + ], + invalid: [ + { + code: 'const [Array] = args;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const {0:Array} = args;' + }, + { + code: 'const [ , res] = args;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const { 1:res} = args;', + }, + { + code: '[, elem] = options;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: '({ 1:elem} = options);', + }, + { + code: 'const {values:[elem]} = options;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const {values:{0:elem}} = options;', + }, + { + code: '[[[elem]]] = options;', + errors: [ + { message: USE_OBJ_DESTRUCTURING }, + { message: USE_OBJ_DESTRUCTURING }, + { message: USE_OBJ_DESTRUCTURING }, + ], + output: '({0:[[elem]]} = options);', + }, + { + code: '[, ...rest] = options;', + errors: [{ message: USE_ARRAY_METHODS }], + }, + { + code: 'for(const [key, value] of new Map);', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'for(const {0:key, 1:value} of new Map);', + }, + { + code: 'let [first,,,fourth] = array;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'let {0:first,3:fourth} = array;', + }, + { + code: 'let [,second,,fourth] = array;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'let {1:second,3:fourth} = array;', + }, + { + code: 'let [ ,,,fourth ] = array;', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'let { 3:fourth } = array;', + }, + { + code: 'let [,,,fourth, fifth,, minorFall, majorLift,...music] = arr;', + errors: [{ message: USE_ARRAY_METHODS }], + }, + { + code: 'function map([key, value]) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function map({0:key, 1:value}) {}', + }, + { + code: 'function map([key, value],) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function map({0:key, 1:value},) {}', + }, + { + code: '(function([key, value]) {})', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: '(function({0:key, 1:value}) {})', + }, + { + code: '(function([key, value] = [null, 0]) {})', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: '(function({0:key, 1:value} = [null, 0]) {})', + }, + { + code: 'function map([key, ...values]) {}', + errors: [{ message: USE_ARRAY_METHODS }], + }, + { + code: 'function map([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function map({0:key, 1:value}, ...args) {}', + }, + { + code: 'async function map([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'async function map({0:key, 1:value}, ...args) {}', + }, + { + code: 'async function* generator([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'async function* generator({0:key, 1:value}, ...args) {}', + }, + { + code: 'function* generator([key, value], ...args) {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'function* generator({0:key, 1:value}, ...args) {}', + }, + { + code: 'const cb = ([key, value], ...args) => {}', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'const cb = ({0:key, 1:value}, ...args) => {}', + }, + { + code: 'class name{ method([key], ...args){} }', + errors: [{ message: USE_OBJ_DESTRUCTURING }], + output: 'class name{ method({0:key}, ...args){} }', + }, + ] + }); diff --git a/tools/eslint-rules/no-array-destructuring.js b/tools/eslint-rules/no-array-destructuring.js new file mode 100644 index 00000000000000..81667e698fbaad --- /dev/null +++ b/tools/eslint-rules/no-array-destructuring.js @@ -0,0 +1,83 @@ +/** + * @fileoverview Iterating over arrays should be avoided because it relies on + * user-mutable global methods (`Array.prototype[Symbol.iterator]` + * and `%ArrayIteratorPrototype%.next`), we should instead use + * other alternatives. This file defines a rule that disallow + * array destructuring syntax in favor of object destructuring + * syntax. Note that you can ignore this rule if you are using + * the array destructuring syntax over a safe iterable, or + * actually want to iterate over a user-provided object. + * @author aduh95 + */ +'use strict'; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +const USE_OBJ_DESTRUCTURING = + 'Use object destructuring instead of array destructuring.'; +const USE_ARRAY_METHODS = + 'Use primordials.ArrayPrototypeSlice to avoid unsafe array iteration.'; + +const findComma = (sourceCode, elements, i, start) => { + if (i === 0) + return sourceCode.getTokenAfter(sourceCode.getTokenByRangeStart(start)); + + let element; + const originalIndex = i; + while (i && !element) { + element = elements[--i]; + } + let token = sourceCode.getTokenAfter( + element ?? sourceCode.getTokenByRangeStart(start) + ); + for (; i < originalIndex; i++) { + token = sourceCode.getTokenAfter(token); + } + return token; +}; +const createFix = (fixer, sourceCode, { range: [start, end], elements }) => [ + fixer.replaceTextRange([start, start + 1], '{'), + fixer.replaceTextRange([end - 1, end], '}'), + ...elements.map((node, i) => + (node === null ? + fixer.remove(findComma(sourceCode, elements, i, start)) : + fixer.insertTextBefore(node, i + ':')) + ), +]; +const arrayPatternContainsRestOperator = ({ elements }) => + elements?.find((node) => node?.type === 'RestElement'); + +module.exports = { + meta: { + type: 'suggestion', + fixable: 'code', + schema: [], + }, + create(context) { + const sourceCode = context.getSourceCode(); + + return { + ArrayPattern(node) { + const hasRest = arrayPatternContainsRestOperator(node); + context.report({ + message: hasRest ? USE_ARRAY_METHODS : USE_OBJ_DESTRUCTURING, + node: hasRest || node, + fix: hasRest ? + undefined : + (fixer) => [ + ...(node.parent.type === 'AssignmentExpression' ? + [ + fixer.insertTextBefore(node.parent, '('), + fixer.insertTextAfter(node.parent, ')'), + ] : + []), + ...createFix(fixer, sourceCode, node), + ], + }); + + }, + }; + }, +};