stylelint-plugin-rhythmguard 3.2.0 → 3.4.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 +31 -0
- package/CONTRIBUTING.md +19 -3
- package/README.md +2 -1
- package/package.json +9 -8
- package/src/audit/args.js +103 -307
- package/src/audit/config.js +1 -1
- package/src/audit/contract.js +6 -28
- package/src/audit/index.js +4 -7
- package/src/audit/render-markdown.js +3 -0
- package/src/audit/render-text.js +2 -1
- package/src/audit/report.js +26 -19
- package/src/audit/scan/files.js +262 -0
- package/src/audit/scan/stylesheets.js +275 -0
- package/src/audit/scan/templates.js +175 -0
- package/src/cli/doctor.js +1 -1
- package/src/cli/quickstart.js +5 -2
- package/src/{utils → core}/length.js +24 -1
- package/src/{utils → core}/options.js +32 -108
- package/src/core/scale-inference.js +560 -0
- package/src/{utils → core}/token-packages.json +7 -0
- package/src/{utils → core}/token-sources.js +155 -17
- package/src/{utils/value-utils.js → core/value-nodes.js} +1 -17
- package/src/eslint/rules/tailwind-class-use-motion-scale.js +2 -2
- package/src/eslint/rules/tailwind-class-use-scale.js +2 -2
- package/src/rules/no-offscale-transform/index.js +22 -91
- package/src/rules/prefer-token/index.js +15 -73
- package/src/rules/report.js +75 -0
- package/src/rules/use-motion-scale/index.js +25 -42
- package/src/rules/use-scale/index.js +21 -94
- package/src/rules/validate.js +132 -0
- package/types/audit.d.ts +11 -0
- package/src/audit/scan.js +0 -676
- package/src/utils/scale-inference.js +0 -351
- /package/src/{utils/constants.js → core/css-vocabulary.js} +0 -0
- /package/src/{utils → core}/tailwind-class-analysis.js +0 -0
- /package/src/{utils → core}/tailwind-motion-analysis.js +0 -0
- /package/src/{utils → core}/time.js +0 -0
- /package/src/{utils → core}/token-map.js +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The Stylelint side of a rule: everything that touches `stylelint.utils` or
|
|
5
|
+
* the PostCSS result lives here, so that src/core stays free of the framework
|
|
6
|
+
* and the same primitives can serve the ESLint plugin and the audit.
|
|
7
|
+
*/
|
|
8
|
+
const stylelint = require('stylelint');
|
|
9
|
+
const { declarationValueIndex } = require('../core/value-nodes');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Compile a user-supplied token pattern. An invalid pattern is reported once
|
|
13
|
+
* against the root and replaced with the historical default so the file still
|
|
14
|
+
* lints instead of throwing inside Stylelint.
|
|
15
|
+
*/
|
|
16
|
+
function createTokenRegex(tokenPattern, result, ruleName) {
|
|
17
|
+
try {
|
|
18
|
+
return new RegExp(tokenPattern);
|
|
19
|
+
} catch {
|
|
20
|
+
stylelint.utils.report({
|
|
21
|
+
message: `Invalid tokenPattern regex: ${tokenPattern}`,
|
|
22
|
+
result,
|
|
23
|
+
ruleName,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return /^--space-/;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Report one value node inside a declaration, positioned on the node rather
|
|
32
|
+
* than the whole declaration, with an optional fix. The default fix replaces
|
|
33
|
+
* the node's text; pass `fix` for anything else.
|
|
34
|
+
*/
|
|
35
|
+
function reportValueNode({ decl, fix = null, length = null, message, node, replacement = null, result, ruleName }) {
|
|
36
|
+
const index = declarationValueIndex(decl) + node.sourceIndex;
|
|
37
|
+
const payload = {
|
|
38
|
+
endIndex: index + (length === null ? node.value.length : length),
|
|
39
|
+
index,
|
|
40
|
+
message,
|
|
41
|
+
node: decl,
|
|
42
|
+
result,
|
|
43
|
+
ruleName,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (fix) {
|
|
47
|
+
payload.fix = fix;
|
|
48
|
+
} else if (replacement) {
|
|
49
|
+
payload.fix = () => {
|
|
50
|
+
node.value = replacement;
|
|
51
|
+
return true;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
stylelint.utils.report(payload);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** A preset name the options did not recognise is reported once, on the root. */
|
|
59
|
+
function reportInvalidPreset(options, { message, result, root, ruleName }) {
|
|
60
|
+
if (!options.invalidPreset) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
stylelint.utils.report({
|
|
64
|
+
message: message(options.invalidPreset, options.presetNames),
|
|
65
|
+
node: root,
|
|
66
|
+
result,
|
|
67
|
+
ruleName,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
createTokenRegex,
|
|
73
|
+
reportInvalidPreset,
|
|
74
|
+
reportValueNode,
|
|
75
|
+
};
|
|
@@ -5,11 +5,10 @@ const valueParser = require('postcss-value-parser');
|
|
|
5
5
|
const {
|
|
6
6
|
nearestScaleValues,
|
|
7
7
|
numbersEqual,
|
|
8
|
-
} = require('../../
|
|
8
|
+
} = require('../../core/length');
|
|
9
9
|
const {
|
|
10
|
-
declarationValueIndex,
|
|
11
10
|
walkRootValueNodes,
|
|
12
|
-
} = require('../../
|
|
11
|
+
} = require('../../core/value-nodes');
|
|
13
12
|
const {
|
|
14
13
|
formatTime,
|
|
15
14
|
fromMs,
|
|
@@ -17,7 +16,10 @@ const {
|
|
|
17
16
|
normalizeDurationUnits,
|
|
18
17
|
parseTimeToken,
|
|
19
18
|
toMs,
|
|
20
|
-
} = require('../../
|
|
19
|
+
} = require('../../core/time');
|
|
20
|
+
|
|
21
|
+
const { reportValueNode } = require('../report');
|
|
22
|
+
const { validatePrimary } = require('../validate');
|
|
21
23
|
|
|
22
24
|
const ruleName = 'rhythmguard/use-motion-scale';
|
|
23
25
|
const DURATION_PROPERTIES = new Set([
|
|
@@ -99,12 +101,7 @@ function functionToString(node) {
|
|
|
99
101
|
|
|
100
102
|
const ruleFunction = (primary, secondaryOptions) => {
|
|
101
103
|
return (root, result) => {
|
|
102
|
-
|
|
103
|
-
actual: primary,
|
|
104
|
-
possible: [true],
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
if (!valid || !validateSecondaryOptions(result, secondaryOptions)) {
|
|
104
|
+
if (!validatePrimary(result, ruleName, primary) || !validateSecondaryOptions(result, secondaryOptions)) {
|
|
108
105
|
return;
|
|
109
106
|
}
|
|
110
107
|
|
|
@@ -120,10 +117,8 @@ const ruleFunction = (primary, secondaryOptions) => {
|
|
|
120
117
|
let changed = false;
|
|
121
118
|
|
|
122
119
|
const reportDuration = (node, nearest, fixedValue = null) => {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
endIndex: index + node.value.length,
|
|
126
|
-
index,
|
|
120
|
+
reportValueNode({
|
|
121
|
+
decl,
|
|
127
122
|
message: nearest
|
|
128
123
|
? messages.rejectedDuration(
|
|
129
124
|
node.value,
|
|
@@ -131,43 +126,31 @@ const ruleFunction = (primary, secondaryOptions) => {
|
|
|
131
126
|
formatTime(nearest.upper, 'ms'),
|
|
132
127
|
)
|
|
133
128
|
: messages.invalidDuration(node.value),
|
|
134
|
-
node
|
|
129
|
+
node,
|
|
130
|
+
replacement: fixedValue,
|
|
135
131
|
result,
|
|
136
132
|
ruleName,
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
if (fixedValue) {
|
|
140
|
-
payload.fix = () => {
|
|
141
|
-
node.value = fixedValue;
|
|
142
|
-
return true;
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
stylelint.utils.report(payload);
|
|
133
|
+
});
|
|
147
134
|
};
|
|
148
135
|
|
|
149
136
|
const reportEasing = (node, replacement = null) => {
|
|
150
137
|
const source = functionToString(node);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
138
|
+
reportValueNode({
|
|
139
|
+
decl,
|
|
140
|
+
fix: replacement
|
|
141
|
+
? () => {
|
|
142
|
+
node.type = 'word';
|
|
143
|
+
node.value = replacement;
|
|
144
|
+
delete node.nodes;
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
: null,
|
|
148
|
+
length: source.length,
|
|
155
149
|
message: messages.rejectedEasing(source),
|
|
156
|
-
node
|
|
150
|
+
node,
|
|
157
151
|
result,
|
|
158
152
|
ruleName,
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
if (replacement) {
|
|
162
|
-
payload.fix = () => {
|
|
163
|
-
node.type = 'word';
|
|
164
|
-
node.value = replacement;
|
|
165
|
-
delete node.nodes;
|
|
166
|
-
return true;
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
stylelint.utils.report(payload);
|
|
153
|
+
});
|
|
171
154
|
};
|
|
172
155
|
|
|
173
156
|
walkRootValueNodes(parsed, (node) => {
|
|
@@ -3,24 +3,19 @@
|
|
|
3
3
|
const stylelint = require('stylelint');
|
|
4
4
|
const valueParser = require('postcss-value-parser');
|
|
5
5
|
const {
|
|
6
|
+
fixedLengthValue,
|
|
6
7
|
formatLength,
|
|
7
|
-
fromPx,
|
|
8
8
|
isHairlineLength,
|
|
9
9
|
nearestScaleValues,
|
|
10
|
-
normalizeScale,
|
|
11
|
-
normalizeScaleByUnit,
|
|
12
10
|
numbersEqual,
|
|
13
11
|
parseLengthToken,
|
|
14
12
|
toPx,
|
|
15
|
-
} = require('../../
|
|
13
|
+
} = require('../../core/length');
|
|
16
14
|
const {
|
|
17
15
|
buildScaleOptions,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
} = require('../../utils/options');
|
|
16
|
+
createPropertyScaleResolver,
|
|
17
|
+
} = require('../../core/options');
|
|
21
18
|
const {
|
|
22
|
-
createTokenRegex,
|
|
23
|
-
declarationValueIndex,
|
|
24
19
|
isKeyword,
|
|
25
20
|
isMathFunction,
|
|
26
21
|
isTokenFunction,
|
|
@@ -28,13 +23,15 @@ const {
|
|
|
28
23
|
shouldLintMathArgument,
|
|
29
24
|
walkRootValueNodes,
|
|
30
25
|
walkTransformTranslateNodes,
|
|
31
|
-
} = require('../../
|
|
26
|
+
} = require('../../core/value-nodes');
|
|
32
27
|
|
|
33
28
|
const {
|
|
34
|
-
DEFAULT_AUTO_TOKEN_PATTERN,
|
|
35
29
|
autoScaleFallbackNote,
|
|
36
|
-
|
|
37
|
-
} = require('../../
|
|
30
|
+
withResolvedScale,
|
|
31
|
+
} = require('../../core/scale-inference');
|
|
32
|
+
|
|
33
|
+
const { createTokenRegex, reportInvalidPreset, reportValueNode } = require('../report');
|
|
34
|
+
const { validatePrimary, validateUseScaleSecondaryOptions } = require('../validate');
|
|
38
35
|
|
|
39
36
|
const ruleName = 'rhythmguard/use-scale';
|
|
40
37
|
|
|
@@ -45,28 +42,6 @@ const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
|
45
42
|
`Unexpected off-scale value "${value}". Use scale values (nearest: ${lower} or ${upper}).${note ? ` ${note}` : ''}`,
|
|
46
43
|
});
|
|
47
44
|
|
|
48
|
-
function getFixedNodeValue(parsedLength, nearestPx, options) {
|
|
49
|
-
const unit = parsedLength.unit || 'px';
|
|
50
|
-
|
|
51
|
-
if (unit === '%' || !options.units.includes(unit)) {
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const signedNearest = parsedLength.number < 0 ? -Math.abs(nearestPx) : nearestPx;
|
|
56
|
-
|
|
57
|
-
if (options.unitStrategy === 'exact') {
|
|
58
|
-
return formatLength(signedNearest, parsedLength.unit || 'px');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const converted = fromPx(signedNearest, unit, options.baseFontSize);
|
|
62
|
-
|
|
63
|
-
if (converted === null) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return formatLength(converted, unit);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
45
|
function checkLengthValue({
|
|
71
46
|
decl,
|
|
72
47
|
node,
|
|
@@ -135,7 +110,7 @@ function checkLengthValue({
|
|
|
135
110
|
}
|
|
136
111
|
|
|
137
112
|
const fixedValue = options.fixToScale
|
|
138
|
-
?
|
|
113
|
+
? fixedLengthValue(parsedLength, nearest.nearest, options)
|
|
139
114
|
: null;
|
|
140
115
|
|
|
141
116
|
report(node.value, decl, node, nearest, fixedValue, unit);
|
|
@@ -159,7 +134,7 @@ function checkLengthValue({
|
|
|
159
134
|
}
|
|
160
135
|
|
|
161
136
|
const fixedValue = options.fixToScale
|
|
162
|
-
?
|
|
137
|
+
? fixedLengthValue(parsedLength, nearest.nearest, options)
|
|
163
138
|
: null;
|
|
164
139
|
|
|
165
140
|
report(node.value, decl, node, nearest, fixedValue, 'px');
|
|
@@ -168,10 +143,7 @@ function checkLengthValue({
|
|
|
168
143
|
|
|
169
144
|
const ruleFunction = (primary, secondaryOptions) => {
|
|
170
145
|
return (root, result) => {
|
|
171
|
-
const valid =
|
|
172
|
-
actual: primary,
|
|
173
|
-
possible: [true],
|
|
174
|
-
});
|
|
146
|
+
const valid = validatePrimary(result, ruleName, primary);
|
|
175
147
|
|
|
176
148
|
if (!valid) {
|
|
177
149
|
return;
|
|
@@ -187,71 +159,26 @@ const ruleFunction = (primary, secondaryOptions) => {
|
|
|
187
159
|
}
|
|
188
160
|
|
|
189
161
|
const options = buildScaleOptions(secondaryOptions);
|
|
190
|
-
|
|
191
|
-
stylelint.utils.report({
|
|
192
|
-
message: messages.invalidPreset(options.invalidPreset, options.presetNames),
|
|
193
|
-
node: root,
|
|
194
|
-
result,
|
|
195
|
-
ruleName,
|
|
196
|
-
});
|
|
197
|
-
}
|
|
162
|
+
reportInvalidPreset(options, { message: messages.invalidPreset, result, root, ruleName });
|
|
198
163
|
|
|
199
|
-
|
|
200
|
-
const inference = resolveAutoScale({
|
|
201
|
-
baseFontSize: options.baseFontSize,
|
|
202
|
-
root,
|
|
203
|
-
scaleSources: options.scaleSources,
|
|
204
|
-
tailwindConfigPath: options.tailwindConfigPath,
|
|
205
|
-
tokenPattern: options.tokenPatternExplicit ? options.tokenPattern : DEFAULT_AUTO_TOKEN_PATTERN,
|
|
206
|
-
});
|
|
207
|
-
options.scale = inference.scale;
|
|
208
|
-
options.scaleInference = inference;
|
|
209
|
-
}
|
|
164
|
+
withResolvedScale(options, root);
|
|
210
165
|
|
|
211
166
|
const tokenRegex = createTokenRegex(options.tokenPattern, result, ruleName);
|
|
212
|
-
const scaleCache = new Map();
|
|
213
167
|
let fallbackNote = autoScaleFallbackNote(options.scaleInference);
|
|
214
|
-
|
|
215
|
-
const getScaleStateForProperty = (prop) => {
|
|
216
|
-
const cached = scaleCache.get(prop);
|
|
217
|
-
if (cached) {
|
|
218
|
-
return cached;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const selectedScale = resolvePropertyScale(prop, options);
|
|
222
|
-
const next = {
|
|
223
|
-
scaleByUnit: normalizeScaleByUnit(selectedScale),
|
|
224
|
-
scalePx: normalizeScale(selectedScale, options.baseFontSize),
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
scaleCache.set(prop, next);
|
|
228
|
-
return next;
|
|
229
|
-
};
|
|
168
|
+
const getScaleStateForProperty = createPropertyScaleResolver(options);
|
|
230
169
|
|
|
231
170
|
const report = (value, decl, node, nearest, fixedValue = null, nearestUnit = 'px') => {
|
|
232
|
-
const index = declarationValueIndex(decl) + node.sourceIndex;
|
|
233
|
-
const endIndex = index + node.value.length;
|
|
234
171
|
const lower = nearest ? formatLength(nearest.lower, nearestUnit) : 'n/a';
|
|
235
172
|
const upper = nearest ? formatLength(nearest.upper, nearestUnit) : 'n/a';
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
endIndex,
|
|
239
|
-
index,
|
|
173
|
+
reportValueNode({
|
|
174
|
+
decl,
|
|
240
175
|
message: messages.rejected(value, lower, upper, fallbackNote),
|
|
241
|
-
node
|
|
176
|
+
node,
|
|
177
|
+
replacement: fixedValue,
|
|
242
178
|
result,
|
|
243
179
|
ruleName,
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
if (fixedValue) {
|
|
247
|
-
payload.fix = () => {
|
|
248
|
-
node.value = fixedValue;
|
|
249
|
-
return true;
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
|
|
180
|
+
});
|
|
253
181
|
fallbackNote = '';
|
|
254
|
-
stylelint.utils.report(payload);
|
|
255
182
|
};
|
|
256
183
|
|
|
257
184
|
root.walkDecls((decl) => {
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Secondary-option validation through Stylelint's `validateOptions`, plus the
|
|
5
|
+
* shape checks it cannot express (arrays and objects). The schemas themselves
|
|
6
|
+
* live in src/core/options.js and know nothing about Stylelint.
|
|
7
|
+
*/
|
|
8
|
+
const stylelint = require('stylelint');
|
|
9
|
+
const {
|
|
10
|
+
NO_OFFSCALE_TRANSFORM_POSSIBLE_OPTIONS,
|
|
11
|
+
NO_OFFSCALE_TRANSFORM_VALIDATION_SCHEMA,
|
|
12
|
+
PREFER_TOKEN_POSSIBLE_OPTIONS,
|
|
13
|
+
PREFER_TOKEN_VALIDATION_SCHEMA,
|
|
14
|
+
USE_SCALE_POSSIBLE_OPTIONS,
|
|
15
|
+
USE_SCALE_VALIDATION_SCHEMA,
|
|
16
|
+
isPlainObject,
|
|
17
|
+
} = require('../core/options');
|
|
18
|
+
|
|
19
|
+
function validateSecondaryOptionShapes(result, ruleName, secondaryOptions, schema) {
|
|
20
|
+
if (secondaryOptions === undefined || secondaryOptions === null) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!isPlainObject(secondaryOptions)) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let valid = true;
|
|
29
|
+
|
|
30
|
+
for (const [optionName, descriptor] of Object.entries(schema)) {
|
|
31
|
+
const optionValue = secondaryOptions[optionName];
|
|
32
|
+
if (optionValue === undefined) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const literalAllowed = Array.isArray(descriptor.allowLiterals)
|
|
37
|
+
&& descriptor.allowLiterals.includes(optionValue);
|
|
38
|
+
|
|
39
|
+
if (descriptor.expectsArray && !Array.isArray(optionValue) && !literalAllowed) {
|
|
40
|
+
valid = false;
|
|
41
|
+
result.warn(
|
|
42
|
+
`Invalid value ${stringifyOptionValue(optionValue)} for option "${optionName}" of rule "${ruleName}"`,
|
|
43
|
+
{ stylelintType: 'invalidOption' },
|
|
44
|
+
);
|
|
45
|
+
result.stylelint.stylelintError = true;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (descriptor.expectsObject && !isPlainObject(optionValue)) {
|
|
50
|
+
valid = false;
|
|
51
|
+
result.warn(
|
|
52
|
+
`Invalid value ${stringifyOptionValue(optionValue)} for option "${optionName}" of rule "${ruleName}"`,
|
|
53
|
+
{ stylelintType: 'invalidOption' },
|
|
54
|
+
);
|
|
55
|
+
result.stylelint.stylelintError = true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return valid;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function stringifyOptionValue(value) {
|
|
63
|
+
if (typeof value === 'string') {
|
|
64
|
+
return `"${value}"`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return `"${JSON.stringify(value)}"`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function validateSecondaryOptions({
|
|
71
|
+
result,
|
|
72
|
+
ruleName,
|
|
73
|
+
secondaryOptions,
|
|
74
|
+
schema,
|
|
75
|
+
possibleOptionMap,
|
|
76
|
+
}) {
|
|
77
|
+
const validOptions = stylelint.utils.validateOptions(result, ruleName, {
|
|
78
|
+
actual: secondaryOptions,
|
|
79
|
+
optional: true,
|
|
80
|
+
possible: possibleOptionMap,
|
|
81
|
+
});
|
|
82
|
+
const validShapes = validateSecondaryOptionShapes(
|
|
83
|
+
result,
|
|
84
|
+
ruleName,
|
|
85
|
+
secondaryOptions,
|
|
86
|
+
schema,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return validOptions && validShapes;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function validateUseScaleSecondaryOptions(result, ruleName, secondaryOptions) {
|
|
93
|
+
return validateSecondaryOptions({
|
|
94
|
+
result,
|
|
95
|
+
ruleName,
|
|
96
|
+
secondaryOptions,
|
|
97
|
+
schema: USE_SCALE_VALIDATION_SCHEMA,
|
|
98
|
+
possibleOptionMap: USE_SCALE_POSSIBLE_OPTIONS,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function validateNoOffscaleTransformSecondaryOptions(result, ruleName, secondaryOptions) {
|
|
103
|
+
return validateSecondaryOptions({
|
|
104
|
+
result,
|
|
105
|
+
ruleName,
|
|
106
|
+
secondaryOptions,
|
|
107
|
+
schema: NO_OFFSCALE_TRANSFORM_VALIDATION_SCHEMA,
|
|
108
|
+
possibleOptionMap: NO_OFFSCALE_TRANSFORM_POSSIBLE_OPTIONS,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function validatePreferTokenSecondaryOptions(result, ruleName, secondaryOptions) {
|
|
113
|
+
return validateSecondaryOptions({
|
|
114
|
+
result,
|
|
115
|
+
ruleName,
|
|
116
|
+
secondaryOptions,
|
|
117
|
+
schema: PREFER_TOKEN_VALIDATION_SCHEMA,
|
|
118
|
+
possibleOptionMap: PREFER_TOKEN_POSSIBLE_OPTIONS,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Every Rhythmguard rule takes `true` as its primary option and nothing else. */
|
|
123
|
+
function validatePrimary(result, ruleName, primary) {
|
|
124
|
+
return stylelint.utils.validateOptions(result, ruleName, { actual: primary, possible: [true] });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = {
|
|
128
|
+
validatePrimary,
|
|
129
|
+
validateNoOffscaleTransformSecondaryOptions,
|
|
130
|
+
validatePreferTokenSecondaryOptions,
|
|
131
|
+
validateUseScaleSecondaryOptions,
|
|
132
|
+
};
|
package/types/audit.d.ts
CHANGED
|
@@ -98,9 +98,19 @@ export interface AuditBaselineComparison {
|
|
|
98
98
|
|
|
99
99
|
export type AuditScaleSource = "default" | "explicit" | "fallback" | "scanned-css" | "token-package" | "token-sources";
|
|
100
100
|
|
|
101
|
+
export interface AuditScaleRejected {
|
|
102
|
+
files?: string[];
|
|
103
|
+
/** Why the inferred set was not accepted as a scale, for example "no common step". */
|
|
104
|
+
reasons: string[];
|
|
105
|
+
source: AuditScaleSource;
|
|
106
|
+
values: Array<number | string>;
|
|
107
|
+
}
|
|
108
|
+
|
|
101
109
|
export interface AuditScale {
|
|
102
110
|
/** Files the scale was derived from (token sources or scanned stylesheets). Empty for explicit, default and fallback. */
|
|
103
111
|
files: string[];
|
|
112
|
+
/** Present when `source` is "fallback" because an inferred scale failed the plausibility check. */
|
|
113
|
+
rejected?: AuditScaleRejected | null;
|
|
104
114
|
source: AuditScaleSource;
|
|
105
115
|
tokenCount: number;
|
|
106
116
|
values: Array<number | string>;
|
|
@@ -139,6 +149,7 @@ export interface AuditContractReport {
|
|
|
139
149
|
files: string[];
|
|
140
150
|
offScaleProperties?: Record<string, number>;
|
|
141
151
|
offScaleValues?: unknown;
|
|
152
|
+
rejected: AuditScaleRejected | null;
|
|
142
153
|
source: AuditScaleSource;
|
|
143
154
|
tokenOpportunities?: unknown;
|
|
144
155
|
values: Array<number | string> | null;
|