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-name-unit-allowed-list #6509

Closed
jeddy3 opened this issue Dec 6, 2022 · 5 comments
Closed

Add media-feature-name-unit-allowed-list #6509

jeddy3 opened this issue Dec 6, 2022 · 5 comments
Labels
status: ready to implement is ready to be worked on by someone type: new rule an entirely new rule

Comments

@jeddy3
Copy link
Member

jeddy3 commented Dec 6, 2022

What is the problem you're trying to solve?

I'd like to enforce specific units for my media features so that they are consistent.

For example, only allowing em units for width media features:

@media (width > 20em) {}

What solution would you like to see?

A new rule to do this.

  • Name: media-feature-name-unit-allowed-list
  • Primary option: object: { "name": ["array", "of", "units"]|"unit" }
  • Secondary options: none
  • Autofixable: None
  • Message: Unexpected unit "${unit}" for name "${name}"
  • Description: "Specify a list of allowed name and unit pairs within media features."
  • Section: "Enforce conventions" -> "Allowed..."

Spec ref for rule name.

Prior art is our declaration-property-unit-allowed-list rule.

This is another candidate for using a new media query parser (#6501 (comment)).

@mattxwang
Copy link
Member

Realizing this is one of the things I mentioned I'd take up earlier, and I think I have some bandwidth now! Is anybody else working on this? If not, happy to take it on.

This is another candidate for using a new media query parser

Was there any further discussion from the thread? I'm happy to work on this without docs / the low-level only API, but if things are in-progress from @romainmenke's side, perhaps I should wait for that first?

@romainmenke
Copy link
Member

A rule like this would look something like this :

'use strict';

const parse = require('@csstools/media-query-list-parser').parse;
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');
const { TokenType } = require('@csstools/css-tokenizer');
const { isTokenNode } = require('@csstools/css-parser-algorithms');
const { isMediaFeaturePlain, isMediaFeatureRange } = require('@csstools/media-query-list-parser');

const ruleName = 'media-feature-name-unit-allowed-list';

const messages = ruleMessages(ruleName, {
	rejected: (unit, name) => `Unexpected unit "${unit}" for name "${name}"`,
});

const meta = {
	url: 'https://stylelint.io/user-guide/rules/media-feature-name-unit-allowed-list',
};

/** @type {import('stylelint').Rule} */
const rule = (primary) => {
	return (root, result) => {
		const validOptions = validateOptions(result, ruleName, {
			actual: primary,
		});

		if (!validOptions) {
			return;
		}

		root.walkAtRules(/^media$/i, (atRule) => {
			const mediaQueryList = parse(atRule.params);

			// media queries can be lists `@media screen, print`
			mediaQueryList.forEach((mediaQuery) => {
				// walk each media query
				mediaQuery.walk((entry) => {

					// only for `(min-width: 300px)` and `(300px < width < 600px)`
					if (isMediaFeaturePlain(entry.node) || isMediaFeatureRange(entry.node)) {
						const name = entry.node.name.toString()

						// walk the child tree of each matching feature
						entry.node.walk((childEntry) => {

							// only on Dimension tokens
							if (isTokenNode(childEntry.node) && childEntry.node.value[0] === TokenType.Dimension) {
								if (childEntry.node.value[4].unit !== 'px') {
									report({
										message: messages.rejected(childEntry.node.value[4].unit, name),
										node: atRule,
										index: childEntry.node.value[2],
										endIndex: childEntry.node.value[3],
										result,
										ruleName,
									});
								}
							}
						});

						// Already visited the child tree, we can skip walking further here.
						return false;
					}
				});
			});
		});
	};
};

rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = meta;
module.exports = rule;

@jeddy3 jeddy3 added status: ready to implement is ready to be worked on by someone type: new rule an entirely new rule and removed status: needs discussion triage needs further discussion labels Dec 28, 2022
@mattxwang
Copy link
Member

Oh, thanks for the quick response @romainmenke!

Doing a bit of travel right now, but if you two are alright waiting a few days, I'll:

  • touch up the comment above and add @romainmenke as a commit co-author
  • get some tests in
  • write the docs
  • open a PR!

If it ends up being urgent, feel free to proceed without me. Thanks for the quick response both of you!

@jeddy3
Copy link
Member Author

jeddy3 commented Dec 29, 2022

Thanks @mattxwang.

If it ends up being urgent

Take your time, there's no rush in open source 😄

@mattxwang
Copy link
Member

Closed by #6550 (merged into v15).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: ready to implement is ready to be worked on by someone type: new rule an entirely new rule
Development

No branches or pull requests

3 participants