linter-bundle 7.9.0 → 7.11.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,24 @@ 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.11.0...HEAD)
10
+
11
+ ## [7.11.0] - 2025-11-19
12
+
13
+ - [eslint] Update `eslint-plugin-jsdoc`from `61.2.1` to `61.3.0`
14
+ - [eslint/jest] Report Jest version detection output only once (instead of once per thread)
15
+ - [stylelint] Added "transform-box", "transition-behavior" and "interpolate-size" to `order/properties-order` rule, and moved "content-visibility" behind "display", and "box-sizing" behind "all"
16
+
17
+ [Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v7.10.0...v7.11.0)
18
+
19
+ ## [7.10.0] - 2025-11-19
20
+
21
+ - [eslint] Updated `@stylistic/eslint-plugin` from `5.6.0` to `5.6.1`
22
+ - [markdown] Updated `markdownlint-cli` from `0.45.0` to `0.46.0``
23
+ - [markdown] Added but disabled [`MD060 - Table column style`](https://github.com/DavidAnson/markdownlint/blob/main/doc/md060.md) rule
24
+ - [stylelint] Added configurable `allowCombinators` option to the `plugin/selector-tag-no-without-class` rule and enabled `>`, `+`, `~` combinators for CSS Modules to avoid false positives
25
+
26
+ [Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v7.9.0...v7.10.0)
10
27
 
11
28
  ## [7.9.0] - 2025-11-18
12
29
 
@@ -52,6 +69,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
52
69
  - [stylelint] Ignore property casing for `composes` in `value-keyword-case`rule
53
70
  - [stylelint] Add `composes` to the ignored property list of the `scss/property-no-unknown` rule
54
71
 
72
+ [Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v7.8.0...v7.9.0)
73
+
55
74
  ## [7.8.0] - 2025-08-21
56
75
 
57
76
  ### Changed
@@ -1657,7 +1676,7 @@ Beside these changes:
1657
1676
  ### Changed
1658
1677
 
1659
1678
  - [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
1679
+ - [eslint] Updated `eslint-plugin-jsdoc` from v34.8.2 to v35.0.0
1661
1680
  - [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
1681
 
1663
1682
  [Show all code changes](https://github.com/jens-duttke/linter-bundle/compare/v1.20.0...v1.21.0)
package/eslint/jest.mjs CHANGED
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import { createRequire } from 'node:module';
6
+ import { isMainThread } from 'node:worker_threads';
6
7
 
7
8
  import jestPlugin from 'eslint-plugin-jest';
8
9
  import globals from 'globals';
@@ -165,12 +166,16 @@ async function getJestVersion () {
165
166
  const jest = ('default' in jestModule ? jestModule.default : jestModule);
166
167
  const version = jest.getVersion().split('.')[0];
167
168
 
168
- process.stdout.write(`Detected Jest version: ${version}\n\n`);
169
+ if (isMainThread) {
170
+ process.stdout.write(`Detected Jest version: ${version}\n\n`);
171
+ }
169
172
 
170
173
  return version;
171
174
  }
172
175
  catch {
173
- process.stderr.write('No Jest version detected\n\n');
176
+ if (isMainThread) {
177
+ process.stderr.write('No Jest version detected\n\n');
178
+ }
174
179
 
175
180
  return 'detect';
176
181
  }
@@ -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.11.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",
@@ -51,7 +51,7 @@
51
51
  "eslint-plugin-functional": "9.0.2",
52
52
  "eslint-plugin-import": "2.32.0",
53
53
  "eslint-plugin-jest": "29.1.0",
54
- "eslint-plugin-jsdoc": "61.2.1",
54
+ "eslint-plugin-jsdoc": "61.3.0",
55
55
  "eslint-plugin-jsx-a11y": "6.10.2",
56
56
  "eslint-plugin-n": "17.23.1",
57
57
  "eslint-plugin-promise": "7.2.1",
@@ -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
  {
@@ -577,14 +577,15 @@ export default {
577
577
  'order/properties-order': [
578
578
  [
579
579
  {
580
- groupName: 'Reset',
580
+ groupName: 'Reset & Defaults',
581
581
  emptyLineBefore: 'always',
582
582
  noEmptyLineBetween: true,
583
583
  properties: [
584
- 'all'
584
+ 'all',
585
+ 'box-sizing',
586
+ 'interpolate-size'
585
587
  ]
586
588
  },
587
-
588
589
  {
589
590
  groupName: 'Performance Optimizations',
590
591
  emptyLineBefore: 'always',
@@ -611,6 +612,7 @@ export default {
611
612
  noEmptyLineBetween: true,
612
613
  properties: [
613
614
  'display',
615
+ 'content-visibility',
614
616
  'visibility',
615
617
 
616
618
  'appearance',
@@ -635,9 +637,7 @@ export default {
635
637
  'inset-block-end',
636
638
  'inset-inline',
637
639
  'inset-inline-start',
638
- 'inset-inline-end',
639
-
640
- 'box-sizing'
640
+ 'inset-inline-end'
641
641
  ]
642
642
  },
643
643
  {
@@ -829,6 +829,7 @@ export default {
829
829
  properties: [
830
830
  'transform',
831
831
  'transform-origin',
832
+ 'transform-box',
832
833
  'transform-style',
833
834
  'backface-visibility',
834
835
  'perspective',
@@ -844,7 +845,8 @@ export default {
844
845
  'transition-property',
845
846
  'transition-duration',
846
847
  'transition-timing-function',
847
- 'transition-delay'
848
+ 'transition-delay',
849
+ 'transition-behavior'
848
850
  ]
849
851
  },
850
852
  {
@@ -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
  }