stylelint-plugin-rhythmguard 3.10.0 → 3.10.1
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 +6 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/audit/render-markdown.js +2 -1
- package/src/audit/token-ladders.js +33 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,12 @@ The format follows Keep a Changelog principles and semantic versioning.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [3.10.1] - 2026-10-15
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Token ladders: a ladder whose every step is a `var()` alias of a step on one other ladder, such as Tailwind `@theme` names over a token file, is folded into that ladder instead of being reported again with the same problems. The ladder it aliases lists it under `aliases`, and the Markdown report names it next to that ladder.
|
|
14
|
+
|
|
9
15
|
## [3.10.0] - 2026-10-15
|
|
10
16
|
|
|
11
17
|
### Added
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
Nobody chose 13px. Rhythmguard catches off-scale spacing in CSS and Tailwind class strings, tells you the nearest steps on your scale, and snaps to them or to your tokens when you ask.
|
|
8
8
|
|
|
9
9
|
[](https://github.com/petrilahdelma/stylelint-plugin-rhythmguard/actions/workflows/ci.yml)
|
|
10
|
-
[](https://www.npmjs.com/package/stylelint-plugin-rhythmguard)
|
|
11
11
|
[](https://www.npmjs.com/package/stylelint-plugin-rhythmguard)
|
|
12
12
|
[](./LICENSE)
|
|
13
13
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stylelint-plugin-rhythmguard",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.1",
|
|
4
4
|
"description": "Nobody chose 13px. Catches off-scale spacing in CSS and Tailwind class strings and snaps it to your scale or tokens. Stylelint rules, an ESLint companion, and an audit CLI.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"rhythmguard": "src/cli/index.js"
|
|
@@ -141,7 +141,8 @@ function appendTokenLaddersMarkdown(lines, ladders) {
|
|
|
141
141
|
const note = `${LADDER_PROBLEM_TEXT[problem.type]}${where} with \`${problem.tokens[0]}\``;
|
|
142
142
|
flagged.set(problem.tokens[1], [...(flagged.get(problem.tokens[1]) || []), note]);
|
|
143
143
|
}
|
|
144
|
-
|
|
144
|
+
const aliasNote = entry.aliases ? ` (also aliased as ${entry.aliases.map((alias) => `\`${escapeMarkdown(alias)}\``).join(', ')})` : '';
|
|
145
|
+
lines.push(`**\`${escapeMarkdown(entry.prefix)}\`**${aliasNote}`);
|
|
145
146
|
lines.push('');
|
|
146
147
|
lines.push('| Token | Min | Max | Problem |');
|
|
147
148
|
lines.push('| --- | --- | --- | --- |');
|
|
@@ -19,7 +19,8 @@ const { formatLength, numbersEqual, parseLengthToken, toPx } = require('../core/
|
|
|
19
19
|
* Each problem names the end it occurs at: `min`, `max`, or `both`.
|
|
20
20
|
*
|
|
21
21
|
* A step that cannot be evaluated (calc(), a viewport length, a token with
|
|
22
|
-
* several different definitions) is left out of the comparison.
|
|
22
|
+
* several different definitions) is left out of the comparison. A ladder
|
|
23
|
+
* that only aliases another one step for step is folded into it.
|
|
23
24
|
*/
|
|
24
25
|
const DEFAULT_MIN_STEP_PX = 2;
|
|
25
26
|
const MIN_LADDER_STEPS = 3;
|
|
@@ -158,9 +159,37 @@ function resolveTokenLadders({ baseFontSize = 16, declarations, minStepPx = DEFA
|
|
|
158
159
|
ladder.steps.push({ max: range.max, min: range.min, rank: step.rank, token });
|
|
159
160
|
}
|
|
160
161
|
|
|
162
|
+
const eligible = [...ladders.values()].filter((ladder) => ladder.steps.length >= MIN_LADDER_STEPS);
|
|
163
|
+
const ladderOfToken = new Map();
|
|
164
|
+
for (const ladder of eligible) {
|
|
165
|
+
for (const step of ladder.steps) ladderOfToken.set(step.token, ladder);
|
|
166
|
+
}
|
|
167
|
+
// A ladder whose every step is a var() alias of a step on one other ladder
|
|
168
|
+
// (Tailwind @theme names over a token file) has that ladder's problems, not
|
|
169
|
+
// its own. It is listed under the other ladder's `aliases` and not counted.
|
|
170
|
+
const aliasTarget = (ladder) => {
|
|
171
|
+
let target = null;
|
|
172
|
+
for (const step of ladder.steps) {
|
|
173
|
+
const values = declarations.get(step.token);
|
|
174
|
+
const reference = values && values.size === 1 ? [...values][0].trim().match(VAR_REFERENCE) : null;
|
|
175
|
+
const owner = reference ? ladderOfToken.get(reference[1]) : null;
|
|
176
|
+
if (!owner || owner === ladder || (target && owner !== target)) return null;
|
|
177
|
+
target = owner;
|
|
178
|
+
}
|
|
179
|
+
return target;
|
|
180
|
+
};
|
|
181
|
+
const aliases = new Map();
|
|
182
|
+
for (const ladder of eligible) {
|
|
183
|
+
const target = aliasTarget(ladder);
|
|
184
|
+
if (target && !aliasTarget(target)) {
|
|
185
|
+
aliases.set(ladder, target);
|
|
186
|
+
target.aliases = [...(target.aliases || []), ladder.prefix].sort();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
161
190
|
const entries = [];
|
|
162
|
-
for (const ladder of
|
|
163
|
-
if (ladder
|
|
191
|
+
for (const ladder of eligible) {
|
|
192
|
+
if (aliases.has(ladder)) continue;
|
|
164
193
|
const steps = ladder.steps.sort((a, b) => a.rank - b.rank);
|
|
165
194
|
const problems = [];
|
|
166
195
|
for (let index = 0; index < steps.length - 1; index += 1) {
|
|
@@ -176,6 +205,7 @@ function resolveTokenLadders({ baseFontSize = 16, declarations, minStepPx = DEFA
|
|
|
176
205
|
if (atMax) problems.push({ end: 'max', tokens, ...atMax });
|
|
177
206
|
}
|
|
178
207
|
entries.push({
|
|
208
|
+
...(ladder.aliases ? { aliases: ladder.aliases } : {}),
|
|
179
209
|
prefix: ladder.prefix,
|
|
180
210
|
problems,
|
|
181
211
|
steps: steps.map(({ max, min, token }) => ({
|