dperini / nwsapi

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

:scope not working when the context has a class name that uses special characters #106

Open jordimarimon opened 6 months ago

jordimarimon commented 6 months ago

Minimal example that breaks in the latest nwsapi (I have downloaded the nwsapi.js file from github today):

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>test nwsapi</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style>
            @media (min-width: 768px) {
                .md\:p-4 {
                    padding: 1rem;
                }
            }
        </style>
    </head>
    <body class="md:p-4">

        <div data-test="foo"></div>

        <script type="text/javascript" src="nwsapi.js" onload="NW.Dom.install()"></script>

        <script>
            console.log(document.body.querySelector(":scope > [data-test=\"foo\"]"));
        </script>>

    </body>
</html>

This throws Uncaught DOMException: unknown pseudo-class selector ':p-4>[data-test="foo"]' but if I try the query selector API provided by the browser it works.

I'm using tailwind which makes use of special characters in the class names and I was running tests with jest using jsdom as the environment and it's when I encounter the error.

Is this use case something that nwsapi would be open to support?

I will try to help by finding out how this could be fixed and in case that I do find it, I could open a PR if it's okay?

jordimarimon commented 6 months ago

I think I have found where the error is thrown:

https://github.com/dperini/nwsapi/blob/master/src/nwsapi.js#L896-L1277

I think it's because :p-4 is not a valid pseudo class. If I were open to help, does nwsapi would want to support class names that use :?

jordimarimon commented 6 months ago

After looking more into it, I have found one possible solution, although maybe it's not the way you want to approach this.

I have added in the makeref implementation to escape the class name:

// replace ':scope' pseudo-class with element references
  makeref =
    function(selectors, element) {
      // DOCUMENT_NODE (9)
      if (element.nodeType === 9) {
        element = element.documentElement;
      }

      return selectors.replace(/:scope/ig,
        element.localName +
        (element.id ? '#' + element.id : '') +
        (element.className ? '.' + escape(element.classList[0]) : ''));
    },

  // This is not the correct way to escape special characters in an identifier, it's just a proof of concept
  escape = function(string) {
    return string.replace(/[!"#$%&'()*+,./;<=>?@\[\\\]^`{|}~\t\n\v\f:]/, match => '\\' + match);
  },

Because when using :scope in the selector, the class name is being retrieved from the DOM, it's not properly escaped.

I think nwsapi should escape the class name when replacing :scope. Also, the id may need to be escaped as well.

dperini commented 6 months ago

@jordimarimon thank you so much for the contribution. Like you did, I would have also escaped special characters

This fix will be in next revision. I will commit as soon as I am back to my desk. By an unlucky accident nwsapi-2.2.8 should be postponed for almost two more weeks.