stylelint-plugin-rhythmguard 3.10.2 → 3.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 +21 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/audit/args.js +22 -2
- package/src/audit/codemod.js +19 -8
- package/src/audit/config.js +17 -2
- package/src/audit/report.js +2 -6
- package/src/audit/scan/files.js +36 -21
- package/src/audit/scan/stylesheets.js +20 -0
- package/src/cli/audit.js +7 -3
- package/src/cli/fix.js +50 -6
- package/src/core/length.js +15 -0
- package/src/core/options.js +1 -0
- package/src/core/tailwind-class-analysis.js +2 -2
- package/src/core/token-index.js +73 -24
- package/src/core/token-map.js +23 -10
- package/src/eslint/literal-spans.js +234 -0
- package/src/eslint/rules/tailwind-class-use-motion-scale.js +9 -32
- package/src/eslint/rules/tailwind-class-use-scale.js +9 -32
- package/src/rules/no-offscale-transform/index.js +3 -2
- package/src/rules/prefer-token/index.js +33 -30
- package/src/rules/report.js +3 -1
- package/src/rules/use-scale/index.js +4 -3
|
@@ -7,6 +7,7 @@ const {
|
|
|
7
7
|
isHairlineLength,
|
|
8
8
|
numbersEqual,
|
|
9
9
|
parseLengthToken,
|
|
10
|
+
pxOnScale,
|
|
10
11
|
toPx,
|
|
11
12
|
} = require('../../core/length');
|
|
12
13
|
const {
|
|
@@ -36,9 +37,9 @@ const { validatePrimary, validatePreferTokenSecondaryOptions } = require('../val
|
|
|
36
37
|
|
|
37
38
|
const {
|
|
38
39
|
negateReplacement,
|
|
40
|
+
signedTokensForLength,
|
|
39
41
|
tokenFitsProperty,
|
|
40
42
|
tokenIndexFromDefinitions,
|
|
41
|
-
tokensForLength,
|
|
42
43
|
} = require('../../core/token-index');
|
|
43
44
|
|
|
44
45
|
const ruleName = 'rhythmguard/prefer-token';
|
|
@@ -61,30 +62,31 @@ function listTokens(tokens) {
|
|
|
61
62
|
: references.join('');
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
/**
|
|
66
|
+
* The token for a raw length, and the text the fix writes. A key with the
|
|
67
|
+
* literal's sign holds exactly that value, so its token is written as is; a
|
|
68
|
+
* negative literal that only matches the positive key gets the token negated.
|
|
69
|
+
* Keys are signed, so a positive literal never finds a negative token (issue
|
|
70
|
+
* #221).
|
|
71
|
+
*/
|
|
69
72
|
function resolveTokenReplacement(tokenMap, raw, parsedLength, options) {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
const unit = parsedLength.unit || 'px';
|
|
74
|
+
const negative = parsedLength.number < 0;
|
|
75
|
+
// [key, negate]: the literal as written, then normalized, then in px.
|
|
76
|
+
const lookups = [[raw, false], [formatLength(parsedLength.number, unit), false]];
|
|
77
|
+
if (negative) lookups.push([formatLength(Math.abs(parsedLength.number), unit), true]);
|
|
78
|
+
if (options.unitStrategy === 'convert') {
|
|
79
|
+
const px = toPx(parsedLength.number, parsedLength.unit, options.baseFontSize);
|
|
80
|
+
if (px !== null) {
|
|
81
|
+
lookups.push([`${px}px`, false]);
|
|
82
|
+
if (negative) lookups.push([`${Math.abs(px)}px`, true]);
|
|
83
|
+
}
|
|
79
84
|
}
|
|
80
85
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (Object.prototype.hasOwnProperty.call(tokenMap, pxKey)) {
|
|
86
|
-
return found(tokenMap[pxKey]);
|
|
87
|
-
}
|
|
86
|
+
for (const [key, negate] of lookups) {
|
|
87
|
+
if (Object.prototype.hasOwnProperty.call(tokenMap, key)) {
|
|
88
|
+
const token = tokenMap[key];
|
|
89
|
+
return { replacement: negate ? negateReplacement(token) : token, token };
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
92
|
|
|
@@ -98,15 +100,16 @@ function resolveTokenReplacement(tokenMap, raw, parsedLength, options) {
|
|
|
98
100
|
* token in another unit, or several tokens, are named but never written.
|
|
99
101
|
*/
|
|
100
102
|
function resolveFromIndex(tokenIndex, parsedLength, prop, options) {
|
|
101
|
-
const px = toPx(
|
|
103
|
+
const px = toPx(parsedLength.number, parsedLength.unit || 'px', options.baseFontSize);
|
|
102
104
|
if (px === null || tokenIndex.length === 0) {
|
|
103
105
|
return { candidates: [] };
|
|
104
106
|
}
|
|
105
107
|
const unit = parsedLength.unit || 'px';
|
|
106
|
-
const sameUnit =
|
|
107
|
-
const
|
|
108
|
+
const sameUnit = signedTokensForLength(tokenIndex, px, unit, { prop });
|
|
109
|
+
const found = sameUnit.tokens.length > 0 || options.unitStrategy !== 'convert'
|
|
108
110
|
? sameUnit
|
|
109
|
-
:
|
|
111
|
+
: signedTokensForLength(tokenIndex, px, unit, { anyUnit: true, prop });
|
|
112
|
+
const candidates = found.tokens;
|
|
110
113
|
if (candidates.length !== 1) {
|
|
111
114
|
return { candidates };
|
|
112
115
|
}
|
|
@@ -114,9 +117,9 @@ function resolveFromIndex(tokenIndex, parsedLength, prop, options) {
|
|
|
114
117
|
const entry = tokenIndex.find((item) => item.token === candidates[0]);
|
|
115
118
|
return {
|
|
116
119
|
candidates,
|
|
117
|
-
fixable: sameUnit.length === 1,
|
|
120
|
+
fixable: sameUnit.tokens.length === 1,
|
|
118
121
|
origin: entry ? entry.file : null,
|
|
119
|
-
replacement:
|
|
122
|
+
replacement: found.negate ? negateReplacement(token) : token,
|
|
120
123
|
token,
|
|
121
124
|
};
|
|
122
125
|
}
|
|
@@ -143,7 +146,7 @@ const ruleFunction = (primary, secondaryOptions) => {
|
|
|
143
146
|
|
|
144
147
|
withResolvedScale(options, root);
|
|
145
148
|
|
|
146
|
-
const tokenRegex = createTokenRegex(options.tokenPattern, result, ruleName);
|
|
149
|
+
const tokenRegex = createTokenRegex(options.tokenPattern, { result, root, ruleName });
|
|
147
150
|
const origins = {};
|
|
148
151
|
const tokenMap = buildEffectiveTokenMap({
|
|
149
152
|
options,
|
|
@@ -253,7 +256,7 @@ const ruleFunction = (primary, secondaryOptions) => {
|
|
|
253
256
|
} else {
|
|
254
257
|
const absPx = toPx(Math.abs(parsedLength.number), parsedLength.unit, options.baseFontSize);
|
|
255
258
|
if (absPx !== null) {
|
|
256
|
-
const onScale = scalePx
|
|
259
|
+
const onScale = pxOnScale(scalePx, absPx, parsedLength.unit, options.baseFontSize);
|
|
257
260
|
if (onScale) {
|
|
258
261
|
return false;
|
|
259
262
|
}
|
package/src/rules/report.js
CHANGED
|
@@ -13,12 +13,14 @@ const { declarationValueIndex } = require('../core/value-nodes');
|
|
|
13
13
|
* against the root and replaced with the historical default so the file still
|
|
14
14
|
* lints instead of throwing inside Stylelint.
|
|
15
15
|
*/
|
|
16
|
-
function createTokenRegex(tokenPattern, result, ruleName) {
|
|
16
|
+
function createTokenRegex(tokenPattern, { result, root, ruleName }) {
|
|
17
17
|
try {
|
|
18
18
|
return new RegExp(tokenPattern);
|
|
19
19
|
} catch {
|
|
20
|
+
// Stylelint throws on a report without a node, so the root carries it (issue #222).
|
|
20
21
|
stylelint.utils.report({
|
|
21
22
|
message: `Invalid tokenPattern regex: ${tokenPattern}`,
|
|
23
|
+
node: root,
|
|
22
24
|
result,
|
|
23
25
|
ruleName,
|
|
24
26
|
});
|
|
@@ -9,6 +9,7 @@ const {
|
|
|
9
9
|
nearestText,
|
|
10
10
|
numbersEqual,
|
|
11
11
|
parseLengthToken,
|
|
12
|
+
pxOnScale,
|
|
12
13
|
toPx,
|
|
13
14
|
} = require('../../core/length');
|
|
14
15
|
const {
|
|
@@ -162,7 +163,7 @@ function checkLengthValue({
|
|
|
162
163
|
return false;
|
|
163
164
|
}
|
|
164
165
|
|
|
165
|
-
const isOnScale = scalePx
|
|
166
|
+
const isOnScale = pxOnScale(scalePx, pxValue, parsedLength.unit, options.baseFontSize);
|
|
166
167
|
if (isOnScale) {
|
|
167
168
|
return false;
|
|
168
169
|
}
|
|
@@ -249,7 +250,7 @@ const ruleFunction = (primary, secondaryOptions) => {
|
|
|
249
250
|
options.decisions = readDecisions(options, { result, root });
|
|
250
251
|
withResolvedScale(options, root);
|
|
251
252
|
|
|
252
|
-
const tokenRegex = createTokenRegex(options.tokenPattern, result, ruleName);
|
|
253
|
+
const tokenRegex = createTokenRegex(options.tokenPattern, { result, root, ruleName });
|
|
253
254
|
if (options.fixWith === 'token') {
|
|
254
255
|
options.tokenIndex = tokenIndexFromDefinitions(
|
|
255
256
|
collectTokenDefinitions({
|
|
@@ -269,7 +270,7 @@ const ruleFunction = (primary, secondaryOptions) => {
|
|
|
269
270
|
const report = (value, decl, node, nearest, fixedValue = null, nearestUnit = 'px', extraNote = '', position = null) => {
|
|
270
271
|
const lower = nearest ? formatLength(nearest.lower, nearestUnit) : 'n/a';
|
|
271
272
|
const upper = nearest ? formatLength(nearest.upper, nearestUnit) : 'n/a';
|
|
272
|
-
const tokenNote = nearest ? tokenHoldsNote(fixedValue, formatLength(nearest.nearest, nearestUnit)) : '';
|
|
273
|
+
const tokenNote = nearest ? tokenHoldsNote(fixedValue, formatLength(nearest.nearest, nearestUnit), String(value).trim().startsWith('-')) : '';
|
|
273
274
|
reportValueNode({
|
|
274
275
|
decl,
|
|
275
276
|
message: messages.rejected(value, lower, upper, [fallbackNote, tokenNote, extraNote, options.note].filter(Boolean).join(' '), position, position ? pxAside(value, options.baseFontSize) : '', getScaleStateForProperty(decl.prop.toLowerCase()).group),
|