dperini / nwsapi

Fast CSS Selectors API Engine
MIT License
103 stars 35 forks source link

Infinite loop fixed but still getting error in selectors #83

Open rocioimpa opened 1 year ago

rocioimpa commented 1 year ago

Hi, the infinite loop issue has been fixed but still getting this test failure in tests that were passing prior to release 2.2.3: SyntaxError: 'style):not(script):not(noscript):not(link):not([tabIndex="-1"]' is not a valid selector

at emit (node_modules/nwsapi/src/nwsapi/js:557:17) at Object._matches [as match] (node_modules/nwsapi/src/nwsapi/js:1400:9) at Array.Resolver (eval at compile (node_modules/nwsapi/src/nwsapi/js:760:17)) at collect (node_modules/nwsapi/src/nwsapi/js:1552:21)

alexgwolff commented 1 year ago

See: https://github.com/dperini/nwsapi/issues/82

workaround:

"overrides": {
    "nwsapi": "2.2.2"
  }
dperini commented 1 year ago

@rocioimpa to be able to help you debugging the issue you reported I need you to write a small code example that can reproduce the error you are getting. However I retested the selector and found it being parsed correctly.

Furthermore I received confirmation from other devs telling the fix resolved the issue. So please double check everything and report back the results you get. Thank you!

cole-adams commented 1 year ago

Hello, I'm also experiencing tests failing in >=2.2.3 that were passing in 2.2.2.

SyntaxError: 'slot):not([inert]' is not a valid selector
      at emit (node_modules/nwsapi/src/nwsapi.js:557:17)
      at Object._matches [as match] (node_modules/nwsapi/src/nwsapi.js:1400:9)
      at Array.Resolver (eval at compile (node_modules/nwsapi/src/nwsapi.js:760:17), <anonymous>:3:105)
      at collect (node_modules/nwsapi/src/nwsapi.js:1552:21)
      at Object._querySelectorAll [as select] (node_modules/nwsapi/src/nwsapi.js:1509:36)

To reproduce:

it("should not throw", () => {
  expect(() =>
    document.querySelectorAll("[tabindex]:not(slot):not([inert])")
  ).not.toThrow();
});
asamuzaK commented 1 year ago

Same here with v2.2.4.

Code:

const nodes = document.querySelectorAll('.foo:not(.bar):not([data-id="1"]');

Result:

SyntaxError: '.foo):not(.bar):not([data-id="1"]' is not a valid selector
      at emit (node_modules\nwsapi\src\nwsapi.js:557:17)
      at Object._matches [as match] (node_modules\nwsapi\src\nwsapi.js:1400:9)
      at Array.Resolver (eval at compile (node_modules\nwsapi\src\nwsapi.js:760:17), <anonymous>:3:105)
      at collect (node_modules\nwsapi\src\nwsapi.js:1552:21)
      at Object._querySelectorAll [as select] (node_modules\nwsapi\src\nwsapi.js:1509:36)
dperini commented 1 year ago

@asamuzaK & @cole-adams both the following DOM queries are passing here on my machines testing what you reported in the console:

document.querySelectorAll("[tabindex]:not(slot):not([inert])")

document.querySelectorAll('.foo:not(.bar):not([data-id="1"]');

are parsed correctly in version 2.2.4 of "nwsapi", not throwing errors in the console. Please ensure you are loading "nwsapi" from the npm repository and not from a secondary fork. If you load "nwsapi.js" in the browser you can verify current version by the following command in console:

NW.Dom.Version

Also, other developers have confirmed that these bugs were fixed, both the infinite recursion and the multiple ":not()". Try using "nwsapi" independently of other packages in browser console first.

asamuzaK commented 1 year ago

Failed log: Update libs asamuzaK/sidebarTabs@99768e5

I'm not using nwsapi directly, I'm using jsdom which depends on nwsapi. In v21.1.1 of jsdom, the nwsapi version is ^2.2.2, so v2.2.4 should be installed when you run tests with GitHub actions.

In package.json, temporary added overrides field and fixed nwsapi version to 2.2.2.

  "overrides": {
    "jsdom": {
      ".": "$jsdom",
      "nwsapi": "2.2.2"
    }
  },

Running tests again, it passed. Add overrides field asamuzaK/sidebarTabs@01b8bb2

So it looks like v2.2.4 still has a bug.

asamuzaK commented 1 year ago

Updated nwsapi to v2.2.4, it failed.

  "overrides": {
    "jsdom": {
      ".": "$jsdom",
      "nwsapi": "2.2.4"
    }
  },

Update nwsapi ยท asamuzaK/sidebarTabs@37c2664

asamuzaK commented 1 year ago

It looks like the regexp in logicalsel is not correct.

Refer to https://github.com/dperini/nwsapi/blob/master/src/nwsapi.js#L1002

console.log(':not(.foo):not(.bar):not([data-baz="1"])'.match(Patterns.logicalsel));

/*
[
  ':not(.foo):not(.bar):not([data-baz="1"])',
  'not',
  '.foo):not(.bar):not([data-baz="1"]',
  '',
  index: 0,
  input: ':not(.foo):not(.bar):not([data-baz="1"])',
  groups: undefined
]
*/
console.log(':not(.foo):not(.bar, [data-baz="1"])'.match(Patterns.logicalsel));

/*
[
  ':not(.foo):not(.bar, [data-baz="1"])',
  'not',
  '.foo):not(.bar, [data-baz="1"]',
  '',
  index: 0,
  input: ':not(.foo):not(.bar, [data-baz="1"])',
  groups: undefined
]
*/
console.log(':not(.foo):not(.bar[data-baz="1"])'.match(Patterns.logicalsel));

/*
[
  ':not(.foo):not(.bar[data-baz="1"])',
  'not',
  '.foo):not(.bar[data-baz="1"]',
  '',
  index: 0,
  input: ':not(.foo):not(.bar[data-baz="1"])',
  groups: undefined
]
*/

If there is an attribute selector at the end of the last :not(), it does not match correctly.

spuente commented 1 year ago

Hi, the infinite loop issue has been fixed but still getting this test failure in tests that were passing prior to release 2.2.3: SyntaxError: 'style):not(script):not(noscript):not(link):not([tabIndex="-1"]' is not a valid selector

at emit (node_modules/nwsapi/src/nwsapi/js:557:17) at Object._matches [as match] (node_modules/nwsapi/src/nwsapi/js:1400:9) at Array.Resolver (eval at compile (node_modules/nwsapi/src/nwsapi/js:760:17)) at collect (node_modules/nwsapi/src/nwsapi/js:1552:21)

Hi @rocioimpa, I was facing a similar issue. The error seems to occur when there's an attribute selector (like [tabIndex="-1"]) inside of a :not and after a selector that is not an attribute (like link, script, .my-class, etc) inside of a :not. I'm not very familiar with the internals of this library, but it seems like the fix for this issue (which was not occurring on v2.2.2) would probably involve some reverts, code updates and generation of a new patch version.

As of now and using v2.2.4, I've found a couple of workarounds that could help:

spuente commented 1 year ago

Hello, I'm also experiencing tests failing in >=2.2.3 that were passing in 2.2.2.

SyntaxError: 'slot):not([inert]' is not a valid selector
      at emit (node_modules/nwsapi/src/nwsapi.js:557:17)
      at Object._matches [as match] (node_modules/nwsapi/src/nwsapi.js:1400:9)
      at Array.Resolver (eval at compile (node_modules/nwsapi/src/nwsapi.js:760:17), <anonymous>:3:105)
      at collect (node_modules/nwsapi/src/nwsapi.js:1552:21)
      at Object._querySelectorAll [as select] (node_modules/nwsapi/src/nwsapi.js:1509:36)

To reproduce:

it("should not throw", () => {
  expect(() =>
    document.querySelectorAll("[tabindex]:not(slot):not([inert])")
  ).not.toThrow();
});

Hi @cole-adams, you could work around this issue by changing the order of the :not selectors to set :not([inert]) before :not(slot):

document.querySelectorAll("[tabindex]:not([inert]):not(slot)")

Or you could group the selectors inside a single :not(...) (See https://github.com/dperini/nwsapi/wiki/CSS-supported-selectors#logical-combination-pseudo-classes-selectors). In this case, the order won't matter, so both of the following snippets should work:

document.querySelectorAll("[tabindex]:not([inert], slot)")
document.querySelectorAll("[tabindex]:not(slot, [inert])")

I'm not very familiar with the internals of this library, so the above suggestions are just meant to work around the issue as a consumer of the library. I hope someone with more context of this codebase can find a definite fix, as this issue was not present on v2.2.2.

asamuzaK commented 1 year ago

:is() takes <forgiving-selector-list> as an argument, while :not() takes <complex-real-selector-list>. IMHO, you should use different regexp for :is() and :not().

stefcameron commented 1 year ago

Or you could group the selectors inside a single :not(...) (See https://github.com/dperini/nwsapi/wiki/CSS-supported-selectors#logical-combination-pseudo-classes-selectors). In this case, the order won't matter, so both of the following snippets should work:

document.querySelectorAll("[tabindex]:not([inert], slot)")
document.querySelectorAll("[tabindex]:not(slot, [inert])")

I'm not very familiar with the internals of this library, so the above suggestions are just meant to work around the issue as a consumer of the library. I hope someone with more context of this codebase can find a definite fix, as this issue was not present on v2.2.2.

This suggestion involves using :not() with multiple arguments, which is not widely-enough supported yet across browsers.

We could change the order of the selectors as suggested here in https://github.com/focus-trap/tabbable/blob/master/src/index.js#L12

Hi @cole-adams, you could work around this issue by changing the order of the :not selectors to set :not([inert]) before :not(slot):

document.querySelectorAll("[tabindex]:not([inert]):not(slot)")

but since the order should not matter, it seems like not the right thing to do (or at least not something that should be necessary to do). I think nswapi should fix the bug instead of tabbable (and who knows how many other libraries) changing the order of their selectors just to make it work, aside from trying to mitigate this issue until it's fixed.

I might see if I can apply that v2.2.2 override to tabbable's package.json instead and then wait for the fix here.

stefcameron commented 1 year ago

tabbable@v6.1.2 has been published which temporarily overrides nwsapi and pins it to v2.2.2 which does not cause the issue. I hope that helps folks while we wait for a fix here.

dperini commented 1 year ago

@stefcameron I am really sorry for the troubles this gives you and the other users, however I am working on a fix for the multiple :not() selectors and specifically when one of them contains an attribute. Also suggestions are welcome and appreciated.

stefcameron commented 1 year ago

@dperini Thank you for working to fix the issue! Hopefully my fix takes a bit of pressure off for now. ๐Ÿ˜„

dperini commented 1 year ago

I want to share the regular expresssion that I am currently refining to solve the first part of the problem. The objective is to split the multiple :not() selectors at the right position in the various possible cases.

https://regex101.com/r/QZFy85/1

by adding a ^ (caret sign) at the start of the RE (hooking the RE to the start of the input selector), or adding a $ (dollar sign) at the end of the RE (hooking the RE to the end of the input selector),

I am able to pop out the first or the last :not() selector from the string at the correct place. These selectors will then be handed one by one to Javascript which will resolve the selector series recursively.

Please, if anybody see a missing or failing case or can spot any inconsistency with this strategy report or suggest.

Thank you !

dperini commented 1 year ago

@all please test if the new changes to the Regular Expression works in your cases. This will be in 2.2.5 as soon as everybody confirm their case is resolved by this change.

stefcameron commented 1 year ago

@dperini I don't see a PR open with a fix against this issue. What line of code in nwsapi would you be updating to this new regex you're thinking of using in https://github.com/dperini/nwsapi/issues/83#issuecomment-1533945771 ?

dperini commented 1 year ago

@stefcameron forgive me, I missed this question which is now 3 weeks old, but anyway ... The regex I was talking about is the one in L #81 named "logicalsel", the one in charge of resolving :is(), :not() and :has() pseudo-classes. I finally changed it yesterday in commit 01216f85b9989ce0f1d5b0ee5ce28bb88ea8aaa3

stefcameron commented 1 year ago

@dperini Better late than never! ๐Ÿ˜‰ Thank you for the commit and the updated code. I'll try to give this a try locally later this week when I have time for focus-trap and will report back here with my findings.

stefcameron commented 1 year ago

@dperini I installed nwsapi v2.2.5, replaced that one line with your updated one, and ran our tabbable tests. All passed! Though I'll be honest, they also passed with v2.2.5 and the old Regex as-is -- while they did not when this issue first surfaced. I don't understand why. But all passed with your changes.

asamuzaK commented 1 year ago

https://regex101.com/r/QZFy85/1 :not(:is([foo=bar], .baz[qux])) only matches :is([foo=bar], .baz[qux])

dperini commented 1 year ago

@asamuzaK cannot reproduce currently published copy on nwsapi GitHub repository works for me. Both the followed selector queries:

    :not(:is([foo=bar], .baz[qux]))
    :is(:not([foo=bar], .baz[qux]))

return the same results using "nwsapi" or "native" methods. Please setup a minimal test that shows the problem, maybe I misunderstood where you are testing. On RegEx101 all the test input lines return correct results. PS: there were a few commit to "nwsapi" recently.

asamuzaK commented 1 year ago

The previous comment is the result of testing with https://regex101.com/r/QZFy85/1

  1. go to https://regex101.com/r/QZFy85/1
  2. clear test string
  3. input :not(:is([foo=bar], .baz[qux]))

Expected: :not(:is([foo=bar], .baz[qux])) is highlighted

Actual: :is([foo=bar], .baz[qux]) is highlighted

dperini commented 1 year ago

@asamuzaK I got it now ... sorry for the misunderstanding but 1 month has gone from when I published that message. I should have explained more about that RE. It should apply only to the expression contained in the first logical selector. The matched logical selector (let's say the :not() selector as in this case) is removed before the expression is evaluated and the compound expression is then handed back to Javascript for the recursion to work.

Have a look at the included RE.txt document which I quickly created trying to explain this process:

RE.txt