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

Add media-feature-range-notation #6430

Closed

Conversation

sidverma32
Copy link
Contributor

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

closes #6161

Is there anything in the PR that needs further explanation?

Name: media-feature-range-notation
Primary option: "prefix"|"context"
Secondary options: none
Autofixable: Yes, but only for the "context" option
Message: - Expected ${primary} media feature range notation
Description: "Specify prefix or context notation for media feature ranges."
Extended description: "Media features of the range type can be written using prefixes or the more modern context notation."
Section: "Enforce convention" -> "Media feature"

@changeset-bot
Copy link

changeset-bot bot commented Oct 30, 2022

⚠️ No Changeset found

Latest commit: df26bc7

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

Copy link
Member

@jeddy3 jeddy3 left a comment

Choose a reason for hiding this comment

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

@sidverma32 Thank you for making a start on this rule.

This rule will need to match the description in #6161 (comment) and #6161 (comment), i.e. it should allow our users to enforce either:

prefix notation:

@media (min-width: 600px) {}

or context notation:

@media (width >= 600px) {}

for their media features.

It's probably best if you use one of the existing notation rules as a template. I suggest using the color-function-notation rule as the problem message is in the correct format. Then layer on the logic that traverses media queries from this pull request, and then add the logic to autofix media features in "prefix" notation into "context" notation.

From the description in #6161 (comment):

We'll only want to autofix the "context" primary option as it's the more modern notation that fixes shortcomings with the prefix notation. We can add autofixing later down the line if it's burdensome to add it upfront.

The rule will need to check all media features that define its “type” as “range” in its definition table in the spec. This includes the likes of width, height, aspect-ratio, resolution, color and so on.

We can look to postcss-media-minmax PostCSS plugin for inspiration on how we might implement these as our autofix for the "context" will work on the same constructs as the plugin but do the exact opposite.

As detailed in the developer guide, you'll also need to:

@jeddy3 jeddy3 changed the title Feat: New rule media-feature-range-notation added Add media-feature-range-notation Oct 30, 2022
@jeddy3
Copy link
Member

jeddy3 commented Oct 31, 2022

Looking at this some more, we may want to raise another issue for the autofix and tackle it in a separate pull request as there are some non-trival edge cases, e.g.:

The following:

@media (min-width: 500px) and screen and (max-width: 1200px) {}

should be autofixed it:

@media screen and (500px <= width <= 1200px) {}

Let's focus on reporting problematic patterns in this pull request.

We can rely on the media query parser's limitation of being unable to parse things in range context to keep the rule logic concise.

Something along the lines of:

const RANGE_TYPE_MEDIA_FEATURES = new Set([
  "width",
  "height",
  "aspect-ratio",
  "resolution",
  "color",
]);

root.walkAtRules(/^media$/i, (atRule) => {
  mediaParser(atRule.params).walk(/^media-feature$/i, (mediaFeatureNode) => {
    const { parent, value } = mediaFeatureNode;

    if (!isStandardSyntaxMediaFeature(value)) return;

    if (
      primary === "prefix" &&
      (isPrefixedRangeTypeMediaFeature(value) ||
        !isRangeTypeMediaFeature(value))
    )
      return;

    if (
      primary === "context" &&
      (isRangeContextMediaFeature(value) || !isRangeTypeMediaFeature(value))
    )
      return;

    const index = atRuleParamIndex(atRule) + parent.sourceIndex;
    const endIndex = index + value.parent.length;

    report({
      message: messages.expected(primary),
      node: atRule,
      index,
      endIndex,
      result,
      ruleName,
    });
  });
});

function isPrefixedRangeMediaFeature(mediaFeature) {
  return mediaFeature.startsWith("min-") || mediaFeature.startsWith("max-");
}

function isRangeTypeMediaFeature(mediaFeature) {
  const unprefixedMediaFeature = mediaFeature.replace(/^(min|max)-/, "");
  return RANGE_TYPE_MEDIA_FEATURES.has(unprefixedMediaFeature);
}

@sidverma32 The logic may prove too simplistic, but hopefully it gives you a sense of a direction we could take. The conditionals may need to be cleaned up to take into account discrete type media features like pointer: fine.

@jeddy3
Copy link
Member

jeddy3 commented Oct 31, 2022

To work on the logic of the rule, you're best writing the tests first.

Something along the lines of the following is a good start:

testRule({
	ruleName,
	config: ['context'],
	fix: true,

	accept: [
		{
			code: '@media {}',
			description: 'empty media query',
		},
		{
			code: '@media () {}',
			description: 'empty media feature',
		},
		{
			code: '@media screen {}',
			description: 'keyword',
		},
		{
			code: '@media (color) {}',
			description: 'range type media feature in boolean context',
		},
		{
			code: '@media (pointer: fine) {}',
			description: 'discrete type media feature',
		},
		{
			code: '@media (width >= 1px) {}',
			description: 'range type media feature in context notation',
		},
		{
			code: '@media screen and (width >= 1px) {}',
			description: 'range type media feature in context notation with keyword',
		},
		{
			code: '@media not print, (width >= 1px) {}',
			description: 'range type media feature in context notation in media query list',
		},
		{
			code: '@media (1px <= width >= 2px) {}',
			description: 'range type media feature in context notation with two values',
		},
	],

	reject: [
		{
			code: '@media (min-width: 1px) {}',
			description: 'range type media feature in prefix notation',
			message: messages.expected('context'),
			line: 1,
			column: 12,
			endLine: 1,
			endColumn: 24,
		},
		{
			code: '@media screen and (min-width: 1px) {}',
			description: 'range type media feature in prefix notation with keyword',
			message: messages.expected('context'),
			line: 1,
			column: 12,
			endLine: 1,
			endColumn: 24,
		},
		{
			code: '@media not print, (min-width: 1px) {}',
			description: 'range type media feature in prefix notation in media query list',
			message: messages.expected('context'),
			line: 1,
			column: 12,
			endLine: 1,
			endColumn: 33,
		},
	],
});


testRule({
	ruleName,
	config: ['prefix'],
	fix: true,

	accept: [
		{
			code: '@media {}',
			description: 'empty media query',
		},
		{
			code: '@media () {}',
			description: 'empty media feature',
		},
		{
			code: '@media screen {}',
			description: 'keyword',
		},
		{
			code: '@media (color) {}',
			description: 'range type media feature in boolean context',
		},
		{
			code: '@media (pointer: fine) {}',
			description: 'discrete type media query',
		},
		{
			code: '@media (min-width: 1px) {}',
			description: 'range type media feature in prefix notation',
		},
		{
			code: '@media screen and (min-width: 1px) {}',
			description: 'range type media feature in prefix notation with keyword',
		},
		{
			code: '@media not print, (min-width: 1px) {}',
			description: 'range type media feature in prefix notation in media query list',
		},
	],

	reject: [
		{
			code: '@media (width >= 1px) {}',
			description: 'range type media feature in context notation',
			message: messages.expected('prefix'),
			line: 1,
			column: 12,
			endLine: 1,
			endColumn: 24,
		},
		{
			code: '@media (1px < width >= 2px) {}',
			description: 'range type media feature in context notation with two values',
			message: messages.expected('prefix'),
			line: 1,
			column: 12,
			endLine: 1,
			endColumn: 24,
		},
		{
			code: '@media screen and (width >= 1px) {}',
			description: 'range type media feature in context notation with keyword',
			message: messages.expected('prefix'),
			line: 1,
			column: 12,
			endLine: 1,
			endColumn: 24,
		},
		{
			code: '@media not print, (width >= 1px) {}',
			description: 'range type media feature in context notation in media query list',
			message: messages.expected('prefix'),
			line: 1,
			column: 12,
			endLine: 1,
			endColumn: 33,
		}
	],
});

(Note: positions aren't correct as this is something I quickly rustled up to show the type of code we should be testing against.)

@sidverma32
Copy link
Contributor Author

As per your suggestion, updated the code. Please have a look and guide me how to proceed from here or any other file also need to be updated with this change!

@@ -17,19 +17,27 @@ const lengthUnits = new Set([
'ric',
'rlh',
// Viewport-percentage lengths
'dvb',
Copy link
Contributor

Choose a reason for hiding this comment

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

#6428 has already been merged.

@jeddy3
Copy link
Member

jeddy3 commented Nov 29, 2022

Please have a look and guide me how to proceed from here or any other file also need to be updated with this change!

@sidverma32 Thank you for your progress so far with this pull request. I've picked up the work in #6497 as there were bits left to do that:

  • I lacked the understanding of until I worked them myself
  • would be time-consuming to explain

You can study the changes in #6497 to see what parts are needed for a new rule consistent with our existing rules. Thanks again for getting the ball rolling with this new rule.

@jeddy3 jeddy3 closed this Nov 29, 2022
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.

Add media-feature-range-notation
3 participants