Skip to content
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

Fix function-url-quotes false positives for SCSS variable and @ character #7416

Merged
merged 4 commits into from Dec 24, 2023

Conversation

mattxwang
Copy link
Member

Which issue, if any, is this issue related to?

Closes #7404 (though, see discussion below).

Is there anything in the PR that needs further explanation?

tl;dr: this PR (right now) contains a bandaid fix, but there may be a bigger problem of isStandardSyntaxUrl's heuristic regex not doing what it should be. Would love to get some opinions since I don't have all the context for the util!

long explanation / context for problem

First, to briefly explain why #7404 is happening: function-url-quotes uses isStandardSyntaxUrl to bail out for nonstandard urls within url():

// In url without quotes scss variable can be everywhere
// But in this case it is allowed to use only specific characters
// Also forbidden "/" at the end of url
if (url.includes('$') && IS_SCSS_VARIABLE_IN_URL.test(url) && !url.endsWith('/')) {
return false;
}

The regex IS_SCSS_VARIABLE_IN_URL is:

const IS_SCSS_VARIABLE_IN_URL = /^[$\s\w+\-,./*'"]+$/;

Based off of the name and the comment in the condition, it seems like the purpose of this regex is to capture an SCSS variable within url. Makes sense! In fact, we have tests for SCSS variables + interpolation (which left me very confused as to why adding an @ sign caused an error):

it('sass variable concat with single quotes string', () => {
expect(isStandardSyntaxUrl("$sass-variable + 'foo'")).toBeFalsy();
});

However, I want to point out that the regex is anchored. It only matches if the entire string matches that regex. So, in $sass-variable + 'foo', IS_SCSS_VARIABLE_IN_URL is matching all of $sass-variable + 'foo', instead of just $sass-variable. So, we're not "really" dealing with concatenation; we're just pretending that there is one SCSS variable called $sass-variable + 'foo'. Here's a regex101 explaining this.

This then explains why #7404 occurred: because @ is not part of the character set, IS_SCSS_VARIABLE_IN_URL doesn't match the entire string $asset-prefix + 'assets/images/fa_calculator_blue@2x.png', even though it probably "should" match $asset-prefix. Here is an accompanying regex101.

I've introduced some failing tests in 48485ef to showcase this.

solutions?

The minimum solution to fix this problem is to just add @ to the regex. I did this in 90422c2, which resolves the failing tests in 48485ef. In terms of the above explanation, this now just lets us treat $asset-prefix + 'assets/images/fa_calculator_blue@2x.png' as "one-giant SCSS variable".

However, I'm not super satisfied with this, since (in my eyes) it punts the problem down the line. For example, the similar declaration still has an error (see accompanying regex101):

'https://' + $asset-prefix + 'assets/images/fa_calculator_blue@2x.png'

Unanchoring itself doesn't work, since it will then incorrectly flag strings with $ within them, like this current test case:

http://domain.com:8080/path/to$to/file$2x.ext?$1=$2

So, a "robust"/full solution probably needs to actually parse the value. I'd be happy to spend some time exploring this if we think it's worthwhile, but we could also stick with the current heuristic and add @ to resolve the immediate issue. I'm also cognizant that I may be missing context (e.g. I didn't write the original util). What are our thoughts?

Copy link

changeset-bot bot commented Dec 18, 2023

🦋 Changeset detected

Latest commit: 57c373d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
stylelint Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment on lines +84 to +90
it('url with at sign at the start', () => {
expect(isStandardSyntaxUrl('@url/path')).toBeTruthy();
});
it('url with ampersand in the middle', () => {
it('url with at sign in the middle', () => {
expect(isStandardSyntaxUrl('some/@url/path')).toBeTruthy();
});
it('url with ampersand at the end', () => {
it('url with at sign at the end', () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

Separately fixing typos I noticed!

Comment on lines -140 to -142
it('sass variable concat with escaped single quotes string', () => {
expect(isStandardSyntaxUrl("$sass-variable + 'foo'")).toBeFalsy();
});
Copy link
Member Author

Choose a reason for hiding this comment

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

This is a duplicate test case that doesn't actually escape the quotes!

it('sass variable concat with double quotes string', () => {
expect(isStandardSyntaxUrl("$sass-variable + 'foo'")).toBeFalsy();
expect(isStandardSyntaxUrl('$sass-variable + "foo"')).toBeFalsy();
Copy link
Member Author

Choose a reason for hiding this comment

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

This test case didn't do what it said earlier (?)

@ybiquitous
Copy link
Member

@mattxwang Thanks for the pull request!

First, don't hesitate to make a PR ready for review if you finish implementation. If a PR is still draft, we may ignore it ("Oh, it's not ready"). 😃

Second, I don't think it's necessary to parse SCSS code in isStandardSyntaxUrl(). It's just a heuristic and not expected to be more accurate than needed. If we implemented a parser for SCSS, the code would be much more complex, and the package size would increase. I believe it's a plugin's task like stylelint-scss.

To resolve the example below that you mentioned, adding : to the regex seems enough. Such a case is rare, so I don't believe it's worth the increased code.

'https://' + $asset-prefix + 'assets/images/fa_calculator_blue@2x.png'

In conclusion, I agree with your current solution adding @ to the regex. 👍🏼

@ybiquitous ybiquitous mentioned this pull request Dec 20, 2023
9 tasks
@mattxwang
Copy link
Member Author

First, don't hesitate to make a PR ready for review if you finish implementation. If a PR is still draft, we may ignore it ("Oh, it's not ready"). 😃

Ah good point, my mistake - I'll make sure not to do that in the future!

Second, I don't think it's necessary to parse SCSS code in isStandardSyntaxUrl(). It's just a heuristic and not expected to be more accurate than needed. If we implemented a parser for SCSS, the code would be much more complex, and the package size would increase. I believe it's a plugin's task like stylelint-scss.

To resolve the example below that you mentioned, adding : to the regex seems enough. Such a case is rare, so I don't believe it's worth the increased code.

'https://' + $asset-prefix + 'assets/images/fa_calculator_blue@2x.png'

Gotcha, I agree with the overall rationale. Unfortunately, this actually (by itself) doesn't resolve the problem, since this will still then (incorrectly) trip this condition first:

if ((url.startsWith(`'`) && url.endsWith(`'`)) || (url.startsWith(`"`) && url.endsWith(`"`))) {
if (hasLessInterpolation(url)) {
return false;
}
return true;
}

So, we'd need to shuffle around the order of checks - though that introduces more of a headache 😓

In conclusion, I agree with your current solution adding @ to the regex. 👍🏼

Maybe I'll leave this PR as-is for now, and address the :-related use-case if a user submits a bug?

(with that in mind, have marked this PR as ready to review)

@mattxwang mattxwang marked this pull request as ready for review December 22, 2023 08:39
@ybiquitous
Copy link
Member

Maybe I'll leave this PR as-is for now, and address the :-related use-case if a user submits a bug?

Ya, sounds good for now 👍🏼

Copy link
Member

@ybiquitous ybiquitous left a comment

Choose a reason for hiding this comment

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

Thank you. LGTM 👍🏼

@ybiquitous ybiquitous merged commit cb509a0 into main Dec 24, 2023
16 checks passed
@ybiquitous ybiquitous deleted the fix-function-url-quotes-fp-scss-and-at branch December 24, 2023 04:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Fix function-url-quotes false positives for SCSS variable and @ character
2 participants