@mrhenry/stylelint-mrhenry-attribute-selector-no-unknown 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import stylelint from 'stylelint';
2
2
  import selectorParser from 'postcss-selector-parser';
3
3
  import { data } from './data.mjs';
4
+ import { isRecordOfStringArrays, isString } from './validate-options.mjs';
4
5
 
5
6
  const ruleName = "@mrhenry/stylelint-mrhenry-attribute-selector-no-unknown";
6
7
  const messages = stylelint.utils.ruleMessages(ruleName, {
@@ -17,12 +18,44 @@ const meta = {
17
18
  url: "https://github.com/mrhenry/stylelint-mrhenry/tree/main/packages/attribute-selector-no-unknown"
18
19
  };
19
20
 
20
- const ruleFunction = (primaryOption, secondaryOptionObject, context) => {
21
+ /** @type {import('stylelint').Rule<true|null>} */
22
+ const ruleFunction = (primaryOption, secondaryOption, context) => {
21
23
  return (postcssRoot, postcssResult) => {
22
- if (!primaryOption) {
23
- return;
24
+ const validPrimary = stylelint.utils.validateOptions(postcssResult, ruleName, {
25
+ actual: primaryOption,
26
+ possible: [true]
27
+ });
28
+
29
+ /* c8 ignore next */
30
+ if (!validPrimary) return;
31
+
32
+ const validSecondary = stylelint.utils.validateOptions(postcssResult, ruleName, {
33
+ actual: secondaryOption,
34
+ possible: {
35
+ globalAttributes: [isString],
36
+ attributesByTagName: isRecordOfStringArrays,
37
+ },
38
+ optional: true,
39
+ });
40
+
41
+ /* c8 ignore next */
42
+ if (!validSecondary) return;
43
+
44
+ const userData = {
45
+ /** @type {Set<string>} */
46
+ globalAttributes: new Set(secondaryOption?.globalAttributes ?? []),
47
+ /** @type {Map<string, string[]>} */
48
+ attributesByTagName: new Map(Object.entries(secondaryOption?.attributesByTagName ?? {})),
49
+ /** @type {Set<string>} */
50
+ allAttributes: new Set(secondaryOption?.globalAttributes ?? []),
24
51
  }
25
-
52
+
53
+ for (const attributes of userData.attributesByTagName.values()) {
54
+ for (const attribute of attributes) {
55
+ userData.allAttributes.add(attribute);
56
+ }
57
+ }
58
+
26
59
  postcssRoot.walkRules((rule) => {
27
60
  if (!rule.selector.includes('[')) {
28
61
  return;
@@ -41,8 +74,12 @@ const ruleFunction = (primaryOption, secondaryOptionObject, context) => {
41
74
 
42
75
  // Global attributes are always allowed.
43
76
  if (data.globalAttributes.has(attribute)) return;
77
+ if (userData.globalAttributes.has(attribute)) return;
44
78
 
45
- if (!data.allAttributes.has(attribute)) {
79
+ if (
80
+ !data.allAttributes.has(attribute) &&
81
+ !userData.allAttributes.has(attribute)
82
+ ) {
46
83
  // An unknown attribute that doesn't start with "data-" is always invalid.
47
84
  stylelint.utils.report({
48
85
  message: messages.expected,
@@ -59,13 +96,16 @@ const ruleFunction = (primaryOption, secondaryOptionObject, context) => {
59
96
 
60
97
  const tagName = findTagNameInCompound(node);
61
98
  if (!tagName) {
62
- // Without a tagname we assime that any attribute is allowed.
99
+ // Without a tagname we assume that any attribute is allowed.
63
100
  return;
64
101
  }
65
102
 
66
103
  const attributesByTagName = data.attributesByTagName.get(tagName);
67
104
  if (attributesByTagName && attributesByTagName.includes(attribute)) return;
68
105
 
106
+ const userAttributesByTagName = userData.attributesByTagName.get(tagName);
107
+ if (userAttributesByTagName && userAttributesByTagName.includes(attribute)) return;
108
+
69
109
  // If the tag name is a standard element
70
110
  // we assume that only attributes that are valid for that tag are allowed.
71
111
  stylelint.utils.report({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrhenry/stylelint-mrhenry-attribute-selector-no-unknown",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Warn when unknown attribute selectors are used",
5
5
  "type": "module",
6
6
  "main": "index.mjs",
@@ -18,6 +18,7 @@
18
18
  "files": [
19
19
  "index.mjs",
20
20
  "data.mjs",
21
+ "validate-options.mjs",
21
22
  "LICENSE",
22
23
  "README.md"
23
24
  ],
@@ -26,12 +27,12 @@
26
27
  "stylelint-plugin"
27
28
  ],
28
29
  "dependencies": {
29
- "postcss-selector-parser": "^6.0.11"
30
+ "postcss-selector-parser": "^6.0.15"
30
31
  },
31
32
  "peerDependencies": {
32
33
  "stylelint": "^16.0.0"
33
34
  },
34
35
  "devDependencies": {
35
- "stylelint": "^16.0.2"
36
+ "stylelint": "^16.1.0"
36
37
  }
37
38
  }
@@ -0,0 +1,25 @@
1
+ export function isString(value) {
2
+ return typeof value === 'string' || value instanceof String;
3
+ }
4
+
5
+ export function isRecordOfStringArrays(value) {
6
+ if (!value) return false;
7
+ if (typeof value !== 'object') return false;
8
+ if (Array.isArray(value)) return false;
9
+
10
+ for (const key in value) {
11
+ if (Object.hasOwnProperty.call(value, key)) {
12
+ /* c8 ignore next */
13
+ if (!isString(key)) return false;
14
+
15
+ if (!value[key]) return false;
16
+ if (!Array.isArray(value[key])) return false;
17
+
18
+ for (const attr of value[key]) {
19
+ if (!isString(attr)) return false;
20
+ }
21
+ }
22
+ }
23
+
24
+ return true;
25
+ }