linter-bundle 7.9.0 → 7.10.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/CHANGELOG.md CHANGED
@@ -6,7 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
- [Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v7.8.0...HEAD)
9
+ [Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v7.10.0...HEAD)
10
+
11
+ ## [7.10.0] - 2025-11-19
12
+
13
+ - [eslint] Updated `@stylistic/eslint-plugin` from `5.6.0` to `5.6.1`
14
+ - [markdown] Updated `markdownlint-cli` from `0.45.0` to `0.46.0``
15
+ - [markdown] Added but disabled [`MD060 - Table column style`](https://github.com/DavidAnson/markdownlint/blob/main/doc/md060.md) rule
16
+ - [stylelint] Added configurable `allowCombinators` option to the `plugin/selector-tag-no-without-class` rule and enabled `>`, `+`, `~` combinators for CSS Modules to avoid false positives
17
+
18
+ [Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v7.9.0...v7.10.0)
10
19
 
11
20
  ## [7.9.0] - 2025-11-18
12
21
 
@@ -52,6 +61,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
52
61
  - [stylelint] Ignore property casing for `composes` in `value-keyword-case`rule
53
62
  - [stylelint] Add `composes` to the ignored property list of the `scss/property-no-unknown` rule
54
63
 
64
+ [Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v7.8.0...v7.9.0)
65
+
55
66
  ## [7.8.0] - 2025-08-21
56
67
 
57
68
  ### Changed
@@ -1657,7 +1668,7 @@ Beside these changes:
1657
1668
  ### Changed
1658
1669
 
1659
1670
  - [eslint] Updated `@typescript-eslint` from v4.24.0 to v4.25.0
1660
- - [eslint] Updated `eslint-plugin-jsdoc` from v34.8.2 to v35.0.0
1671
+ - [eslint] Updated `eslint-plugin-jsdoc` from v34.8.2 to v35.0.0
1661
1672
  - [eslint] Activated `ignoreNonDOM` option for [`jsx-a11y/no-autofocus`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-autofocus.md) rule
1662
1673
 
1663
1674
  [Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v1.20.0...v1.21.0)
@@ -17,5 +17,6 @@
17
17
  "span"
18
18
  ]
19
19
  },
20
- "MD046": false
20
+ "MD046": false,
21
+ "MD060": false
21
22
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linter-bundle",
3
- "version": "7.9.0",
3
+ "version": "7.10.0",
4
4
  "type": "module",
5
5
  "description": "Ready-to use bundle of linting tools, containing configurations for ESLint, stylelint and markdownlint.",
6
6
  "keywords": [
@@ -42,7 +42,7 @@
42
42
  "_test-stylelint": "node ./test-stylelint.js"
43
43
  },
44
44
  "dependencies": {
45
- "@stylistic/eslint-plugin": "5.6.0",
45
+ "@stylistic/eslint-plugin": "5.6.1",
46
46
  "eslint": "9.39.1",
47
47
  "eslint-formatter-unix": "9.0.1",
48
48
  "eslint-import-resolver-typescript": "4.4.4",
@@ -59,7 +59,7 @@
59
59
  "eslint-plugin-react-hooks": "7.0.1",
60
60
  "eslint-plugin-unicorn": "62.0.0",
61
61
  "globals": "16.5.0",
62
- "markdownlint-cli": "0.45.0",
62
+ "markdownlint-cli": "0.46.0",
63
63
  "micromatch": "4.0.8",
64
64
  "postcss-scss": "4.0.9",
65
65
  "stylelint": "16.25.0",
@@ -81,11 +81,5 @@
81
81
  "@types/node": "24.10.1",
82
82
  "stylelint-find-new-rules": "5.0.0",
83
83
  "typescript": "5.9.3"
84
- },
85
- "overrides": {
86
- "glob": "12.0.0"
87
- },
88
- "resolutions": {
89
- "glob": "12.0.0"
90
84
  }
91
85
  }
@@ -130,7 +130,7 @@ export default {
130
130
  *
131
131
  * @see https://github.com/Moxio/stylelint-selector-tag-no-without-class
132
132
  */
133
- 'plugin/selector-tag-no-without-class': ['/./']
133
+ 'plugin/selector-tag-no-without-class': [['/./'], { allowCombinators: ['>', '+', '~'] }]
134
134
  }
135
135
  },
136
136
  {
@@ -22,26 +22,121 @@ const messages = stylelint.utils.ruleMessages(ruleName, {
22
22
  });
23
23
 
24
24
  // @ts-expect-error -- Parameter 'primaryOption' implicitly has an 'any' type.
25
- function rule (primaryOption) {
25
+ function rule (primaryOption, secondaryOptions = {}) {
26
26
  // @ts-expect-error -- Parameter 'root' implicitly has an 'any' type. / Parameter 'result' implicitly has an 'any' type.
27
27
  return (root, result) => {
28
- const validOptions = stylelint.utils.validateOptions(result, ruleName, {
29
- actual: primaryOption,
30
- possible: [(string) => (typeof string === 'string' || Object.prototype.toString.call(string) === '[object String]')]
31
- });
28
+ const validOptions = stylelint.utils.validateOptions(
29
+ result,
30
+ ruleName,
31
+ {
32
+ actual: primaryOption,
33
+ possible: [(string) => (typeof string === 'string' || Object.prototype.toString.call(string) === '[object String]')]
34
+ },
35
+ {
36
+ actual: secondaryOptions,
37
+ possible: {
38
+ allowCombinators: [' ', '+', '>', '~', '||']
39
+ },
40
+ optional: true
41
+ }
42
+ );
32
43
 
33
44
  if (!validOptions) {
34
45
  return;
35
46
  }
36
47
 
48
+ /** @type {{ allowCombinators?: string | string[] }} */
49
+ const typedSecondaryOptions = secondaryOptions;
50
+ const allowCombinatorsOption = typedSecondaryOptions.allowCombinators;
51
+ const allowCombinatorValues = allowCombinatorsOption === undefined ? [] : (Array.isArray(allowCombinatorsOption) ? allowCombinatorsOption : [allowCombinatorsOption]);
52
+ /** @type {string[]} */
53
+ const normalizedAllowedCombinators = [];
54
+ for (const combinator of allowCombinatorValues) {
55
+ if (combinator.length === 0) {
56
+ continue;
57
+ }
58
+
59
+ normalizedAllowedCombinators.push(normalizeCombinatorValue(combinator));
60
+ }
61
+
62
+ const allowedCombinators = new Set(normalizedAllowedCombinators);
63
+
64
+ /**
65
+ * @param {string | undefined} value
66
+ * @returns {string}
67
+ */
68
+ function normalizeCombinatorValue (value) {
69
+ if (typeof value !== 'string') {
70
+ return '';
71
+ }
72
+
73
+ return value.trim() === '' ? ' ' : value.trim();
74
+ }
75
+
76
+ /**
77
+ * @param {import('postcss-selector-parser').Selector} selectorNode
78
+ */
79
+ function splitSelectorIntoSegments (selectorNode) {
80
+ /** @type {{ nodes: import('postcss-selector-parser').Node[], leadingCombinator?: string }[]} */
81
+ const segments = [];
82
+ /** @type {import('postcss-selector-parser').Node[]} */
83
+ let currentSegment = [];
84
+ /** @type {string | undefined} */
85
+ let leadingCombinator;
86
+
87
+ function pushCurrentSegment () {
88
+ if (currentSegment.length === 0) {
89
+ return;
90
+ }
91
+
92
+ const segmentNodes = currentSegment;
93
+ currentSegment = [];
94
+
95
+ /** @type {{ nodes: import('postcss-selector-parser').Node[], leadingCombinator?: string }} */
96
+ const segment = { nodes: segmentNodes };
97
+
98
+ if (leadingCombinator !== undefined) {
99
+ segment.leadingCombinator = leadingCombinator;
100
+ leadingCombinator = undefined;
101
+ }
102
+
103
+ segments.push(segment);
104
+ }
105
+
106
+ selectorNode.each((/** @type {import('postcss-selector-parser').Node} */ child) => {
107
+ if (child.type === 'combinator') {
108
+ pushCurrentSegment();
109
+ leadingCombinator = normalizeCombinatorValue(child.value);
110
+
111
+ return;
112
+ }
113
+
114
+ currentSegment.push(child);
115
+ });
116
+
117
+ pushCurrentSegment();
118
+
119
+ return segments;
120
+ }
121
+
122
+ /**
123
+ * @param {string | undefined} leadingCombinator
124
+ */
125
+ function shouldIgnoreSegment (leadingCombinator) {
126
+ if (!leadingCombinator || allowedCombinators.size === 0) {
127
+ return false;
128
+ }
129
+
130
+ return allowedCombinators.has(leadingCombinator);
131
+ }
132
+
37
133
  // @ts-expect-error -- Parameter 'selectorNode' implicitly has an 'any' type. / Parameter 'ruleNode' implicitly has an 'any' type.
38
134
  function checkSelector (selectorNode, ruleNode) {
39
- // @ts-expect-error -- Parameter 'node' implicitly has an 'any' type.
40
- const combinedSegments = selectorNode.split((node) => (node.type === 'combinator'));
135
+ const segments = splitSelectorIntoSegments(selectorNode);
41
136
 
42
- for (const segment of combinedSegments) {
137
+ for (const segment of segments) {
43
138
  let unqualifiedTagNode;
44
- for (const node of segment) {
139
+ for (const node of segment.nodes) {
45
140
  if (node.type === 'tag' && matchesStringOrRegExp(node.value, primaryOption)) {
46
141
  unqualifiedTagNode = node;
47
142
  }
@@ -50,13 +145,13 @@ function rule (primaryOption) {
50
145
  }
51
146
  }
52
147
 
53
- if (unqualifiedTagNode) {
148
+ if (unqualifiedTagNode && !shouldIgnoreSegment(segment.leadingCombinator)) {
54
149
  stylelint.utils.report({
55
150
  ruleName,
56
151
  result,
57
152
  node: ruleNode,
58
153
  message: messages.unexpected(unqualifiedTagNode.value),
59
- word: unqualifiedTagNode
154
+ word: unqualifiedTagNode.value
60
155
  });
61
156
  }
62
157
  }