Automattic / juice

Juice inlines CSS stylesheets into your HTML source.
MIT License
3.12k stars 221 forks source link

[BUG] It stuck in an infinite loop when parsing specific css. #390

Open greg-md opened 2 years ago

greg-md commented 2 years ago

If you run this, you will never get a response.

const juice = require('juice');
const result = juice(`<style>.my-class:not(.is-content-justification-space-between, .is-content-justification-right) {}</style>`);
console.log('result', result);

If there is only one class inside the :not(...), or if the class names are shorter - it could not be reproduced.

Node version: v10.24.1 Juice version: 8.0.0

filoscoder commented 1 year ago

I think this will solve it https://github.com/Automattic/juice/pull/442

sparga commented 1 year ago

I recently encountered this as well and I believe it's because of an issue with mensch, the library used to transform CSS into tokens. Selectors are split with split(',') which in this situation creates a token like :not(.is-content-justification-space-between with no closing parenthesis.

When this token is passed to the Slick parser, it blocks at this line as the Regex evaluation seems to never completes.

There is a PR open to fix this in mensch (https://github.com/brettstimmerman/mensch/pull/29) but it uses a pattern with negative lookbehind which is not supported on all browsers.

If it helps, here is a test case to reproduce this issue:

.css file:

#root [mstyles='flowchart_container'] :is([mstyles~='flowchart_block_simple'],[mstyles~='flowchart_block_multi'],[mstyles~='flowchart_block_dashed'],[mstyles~='flowchart_icon_container']) {
    background: var(--background);
    border-color: var(--on-background);
}

.html file:

<div id="root"></div>

.out file:

<div id="root"></div>