Skip to content

Fix map spreading #875

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

Closed
wants to merge 2 commits into from
Closed

Fix map spreading #875

wants to merge 2 commits into from

Conversation

quisido
Copy link

@quisido quisido commented Jul 15, 2022

Getting this error in ESLint, which seems perfectly valid:

Invalid attempt to spread non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.
Referenced from: .../.eslintrc.cjs
    at _nonIterableSpread (.../.yarn/cache/@babel-runtime-npm-7.18.6-e238904aef-8b707b64ae.zip/node_modules/@babel/runtime/helpers/nonIterableSpread.js:2:9)
    at _toConsumableArray (.../.yarn/cache/@babel-runtime-npm-7.18.6-e238904aef-8b707b64ae.zip/node_modules/@babel/runtime/helpers/toConsumableArray.js:10:95)
    at Object.<anonymous> (.../.yarn/__virtual__/eslint-plugin-jsx-a11y-virtual-70ad45934f/7/.../.yarn/cache/eslint-plugin-jsx-a11y-npm-6.6.0-d57094bb84-d9da9a3ec7.zip/node_modules/eslint-plugin-jsx-a11y/lib/util/isInteractiveElement.js:24:61)

isInteractiveElement has a couple of instances of [...someMap], where the map is not a literal Map object, but instead is a vanilla object with a couple of hand-written Map prototype methods.

This package then uses Babel to transpile the spread operator, and that very Babel transpilation throws an error that said object cannot be spread.

An alternative solution could be to add the [Symbol.iterator]() method to the faux maps, but this seems more explicitly accurate and faster than nested dependency changes. I used entries() because [...new Map()] returns an array of the map's entries.

@codecov
Copy link

codecov bot commented Jul 15, 2022

Codecov Report

Merging #875 (27de267) into main (0d2bc43) will increase coverage by 0.03%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             main     #875      +/-   ##
==========================================
+ Coverage   99.26%   99.29%   +0.03%     
==========================================
  Files          98      101       +3     
  Lines        1488     1555      +67     
  Branches      492      511      +19     
==========================================
+ Hits         1477     1544      +67     
  Misses         11       11              
Impacted Files Coverage Δ
src/util/isInteractiveElement.js 100.00% <100.00%> (ø)
src/util/getAccessibleChildText.js 100.00% <0.00%> (ø)
src/rules/anchor-ambiguous-text.js 100.00% <0.00%> (ø)
src/rules/prefer-tag-over-role.js 100.00% <0.00%> (ø)

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@ljharb
Copy link
Member

ljharb commented Jul 15, 2022

I'm confused - is your babel configured to transpile node_modules? It's never safe to transform code you didn't author.

Copy link
Member

@ljharb ljharb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I agree this is correct (as is the existing code), I'm confused why it's not working for you, since it's been published this way for awhile.

@ljharb
Copy link
Member

ljharb commented Jul 15, 2022

hm, i also note you're using yarn pnp, which breaks a bunch of things. Does this happen using node's actual resolution mechanism?

@quisido
Copy link
Author

quisido commented Jul 16, 2022

I'm confused - is your babel configured to transpile node_modules? It's never safe to transform code you didn't author.

@ljharb This is not related to the consumer's Babel configuration. The references to Babel in the description refer to this package's own build configuration.

While I agree this is correct (as is the existing code), I'm confused why it's not working for you, since it's been published this way for awhile.

I wish I knew too. It works when executing eslint by command line, but it fails when executing eslint via the VS Code editor. Frankly, I'm more concerned that the code works for anyone, since the error seems completely accurate. 😂 I read the code, and there's no reason this error shouldn't be thrown 100% of the time. It essentially says, "If it's not Array.isArray, instanceof Map, instanceof Set, or have a [Symbol.iterator] method, then throw an error." It definitely doesn't meet any of the criteria, so the error should always be thrown.

I patched my local copy to use .entries() and verified that the error does go away once the update is applied.

hm, i also note you're using yarn pnp, which breaks a bunch of things. Does this happen using node's actual resolution mechanism?

I don't believe it's Yarn PNP, because yarn eslint works as expected. I actually think it may be the Yarn VS Code SDK for ESLint. I don't know why it exclusively would result in this error, because as stated, the error is accurate.

Node is not explicitly installed on the machine that encounters this error, so while yarn eslint uses Node (and passes), the VS Code SDK must be using whatever JS runtime that VS Code itself uses. Electron? Which is probably Node behind-the-scenes? It must be a version of Node that doesn't support this. 🤷‍♂️

I think the most important thing is that this fix is accurate. 👍 If it had to be defined, "Adds support for Electron."

@ljharb
Copy link
Member

ljharb commented Jul 16, 2022

yarn eslint uses PnP, to try it without, you'd need to do npx eslint or ./node_modules/.bin/eslint, but that won't work unless you've installed without PnP.

@ljharb
Copy link
Member

ljharb commented Jul 16, 2022

I agree the fix is accurate; what's mystifying is how you're the first person to report the bug. Additionally, I'm not able to reproduce it - which is why i suspect it might be related to PnP. What version of @babel/runtime do you have installed?

@quisido
Copy link
Author

quisido commented Jul 16, 2022

I don't believe it is a PNP issue because it is working with PNP when I execute it by CLI. It only doesn't work via the Yarn ESLint SDK for VSCode.

I can't confirm the Babel runtime version until Monday, as this occurred on a work device, and I don't want to work before then. 😆

@ljharb
Copy link
Member

ljharb commented Jul 16, 2022

Right - but that's what I'm saying - typically when using PnP, lots of integrations tend to break.

Happy to wait :-) it's important to understand why something is broken before fixing it.

@quisido
Copy link
Author

quisido commented Jul 16, 2022

I'm not sure I align with that perspective. It's broken because it's trying to spread a non-spreadable object. It's clear why it's broken. The part that doesn't make sense is why it works. I do not believe that we first need to understand why it works before fixing why it's broken. This change is backwards compatible.

@ljharb
Copy link
Member

ljharb commented Jul 16, 2022

My hunch is that you're using a version of @babel/runtime that has different behavior - ie, that throws in this case, whereas the latest version of babel does not.

@quisido
Copy link
Author

quisido commented Jul 16, 2022

@babel/runtime is the latest version: 7.18.6.

The Babel file throwing the error hasn't been changed since November 2021.

Investigation steps:

// eslint-plugin-jsx-a11y/lib/util/isInteractiveElement.js
12: var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
24: var elementRoleEntries = (0, _toConsumableArray2["default"])(_ariaQuery.elementRoles);

toConsumableArray is throwing an error when it receives elementRoles (which is a vanilla object, shaped { entries(){}, keys(){}, values(){}, etc }).

// @babel/runtime/helpers/toConsumableArray.js
01: var arrayWithoutHoles = require("./arrayWithoutHoles.js");
03: var iterableToArray = require("./iterableToArray.js");
05: var unsupportedIterableToArray = require("./unsupportedIterableToArray.js");
07: var nonIterableSpread = require("./nonIterableSpread.js");
09: function _toConsumableArray(arr) {
10:  return arrayWithoutHoles(arr) || iterableToArray(arr) || unsupportedIterableToArray(arr) || nonIterableSpread();
11: }

toConsumableArray checks if the value is an "array without holes" (Array.isArray), an "iterable" (has a Symbol.iterator key), an "unsupported iterable" (Map or Set), or else it throws an error (nonIterableSpread is a function that only throws an error):

I don't know why the file toConsumableArray.js is toArray.js in the Babel repo, but the contents are verbatim the same. You should be able to verify these in your local node_modules.

I also found this issue where the maintainers state that plain objects are not iterable and are expected to throw an error when passed to toConsumableArray.

@quisido
Copy link
Author

quisido commented Aug 22, 2022

Sorry, I closed this because I discovered the reason this wasn't impacting others. When I patched eslint-plugin-jsx-a11y to contain aria-query as a peerDependency, I had the wrong version.

This package depends on aria-query@^4, whereas I was using aria-query@^5.

If you want to keep this PR, you should be able to go ahead and bump aria-query to ^5 (and should include it as a peer dependency for Yarn berry support).

@ljharb
Copy link
Member

ljharb commented Aug 22, 2022

ahhh, thanks for clarifying. in that case yes, i'll close this as a duplicate of #814.

@ljharb ljharb closed this Aug 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants