stylelint-plugin-rhythmguard 1.2.1 → 1.3.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 +18 -0
- package/README.md +34 -5
- package/assets/rhythmguard-campaign-60s.gif +0 -0
- package/assets/rhythmguard-campaign-60s.webm +0 -0
- package/package.json +2 -1
- package/src/rules/no-offscale-transform/index.js +13 -1
- package/src/rules/prefer-token/index.js +13 -1
- package/src/rules/use-scale/index.js +13 -1
- package/src/utils/options.js +300 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@ The format follows Keep a Changelog principles and semantic versioning.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.3.0] - 2026-02-17
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Strict `secondaryOptions` validation for all three rules:
|
|
14
|
+
- `rhythmguard/use-scale`
|
|
15
|
+
- `rhythmguard/prefer-token`
|
|
16
|
+
- `rhythmguard/no-offscale-transform`
|
|
17
|
+
- Invalid option names (for example `sevverity`) now fail with Stylelint invalid option warnings instead of silently being ignored.
|
|
18
|
+
- Type/shape validation for option payloads (for example `properties` must be an array, `tokenMap` must be an object).
|
|
19
|
+
- Regression tests for invalid secondary option names and option value shapes.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- Added `known-css-properties` as a direct runtime dependency to guarantee `properties` option validation in consumer installs.
|
|
24
|
+
- `properties` option validation now checks supported spacing property names against known CSS property metadata (plus `translate-x`, `translate-y`, `translate-z`).
|
|
25
|
+
|
|
9
26
|
## [1.2.1] - 2026-02-17
|
|
10
27
|
|
|
11
28
|
### Fixed
|
|
@@ -15,6 +32,7 @@ The format follows Keep a Changelog principles and semantic versioning.
|
|
|
15
32
|
- `rhythmguard/prefer-token` now supports `enforceInsideMathFunctions` for optional math-function enforcement.
|
|
16
33
|
- Hardened `var()` token argument detection to parse the first argument structurally (rather than comma string splitting).
|
|
17
34
|
- npm README link integrity: docs links now resolve to absolute GitHub URLs from the npm package page.
|
|
35
|
+
- Release workflow now detects missing `NPM_TOKEN` and skips publish cleanly with an explicit notice instead of failing.
|
|
18
36
|
|
|
19
37
|
### Added
|
|
20
38
|
|
package/README.md
CHANGED
|
@@ -14,6 +14,16 @@ High-precision spacing governance for CSS and design systems.
|
|
|
14
14
|
|
|
15
15
|
`stylelint-plugin-rhythmguard` enforces spacing discipline across margin, padding, gap, inset, scroll spacing, and translate motion offsets.
|
|
16
16
|
|
|
17
|
+
## 60-second Demo
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="https://raw.githubusercontent.com/petrilahdelma/stylelint-plugin-rhythmguard/main/assets/rhythmguard-campaign-60s.webm">
|
|
21
|
+
<img src="https://raw.githubusercontent.com/petrilahdelma/stylelint-plugin-rhythmguard/main/assets/rhythmguard-campaign-60s.gif" width="100%" alt="Rhythmguard 60-second campaign demo" />
|
|
22
|
+
</a>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
- Full video (WebM): [rhythmguard-campaign-60s.webm](https://raw.githubusercontent.com/petrilahdelma/stylelint-plugin-rhythmguard/main/assets/rhythmguard-campaign-60s.webm)
|
|
26
|
+
|
|
17
27
|
I built Rhythmguard after 20 years of watching teams ignore spacing scales and ship arbitrary pixel values everywhere.
|
|
18
28
|
|
|
19
29
|
It is built for teams that want:
|
|
@@ -146,6 +156,24 @@ Scale resolution precedence:
|
|
|
146
156
|
3. `preset`
|
|
147
157
|
4. default `rhythmic-4` scale
|
|
148
158
|
|
|
159
|
+
## Option Validation
|
|
160
|
+
|
|
161
|
+
Rhythmguard validates `secondaryOptions` for each rule before linting declarations.
|
|
162
|
+
|
|
163
|
+
- Unknown option names fail fast with Stylelint invalid option warnings.
|
|
164
|
+
- Invalid option shapes fail fast (for example string vs array mismatches).
|
|
165
|
+
- `properties` string entries are validated against supported CSS spacing property names, plus `translate-x`, `translate-y`, and `translate-z`.
|
|
166
|
+
|
|
167
|
+
Example typo that now fails immediately:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"rules": {
|
|
172
|
+
"rhythmguard/use-scale": [true, { "sevverity": "warning" }]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
149
177
|
## Built-in Scale Presets
|
|
150
178
|
|
|
151
179
|
| Preset | Pattern | Scale |
|
|
@@ -267,7 +295,7 @@ Options:
|
|
|
267
295
|
| `allowPercentages` | `boolean` | `true` | Allows `%` values without scale checks |
|
|
268
296
|
| `fixToScale` | `boolean` | `true` | Enables nearest-value autofix |
|
|
269
297
|
| `enforceInsideMathFunctions` | `boolean` | `false` | Lints `calc()/clamp()/min()/max()` internals |
|
|
270
|
-
| `properties` | `Array<string|RegExp>` | built-in spacing patterns | Override targeted property set |
|
|
298
|
+
| `properties` | `Array<string|RegExp>` | built-in spacing patterns | Override targeted property set; string values must be supported spacing property names |
|
|
271
299
|
|
|
272
300
|
### `rhythmguard/prefer-token`
|
|
273
301
|
|
|
@@ -303,7 +331,7 @@ Options:
|
|
|
303
331
|
| `enforceInsideMathFunctions` | `boolean` | `false` | Lints `calc()/clamp()/min()/max()` internals |
|
|
304
332
|
| `tokenMap` | `Record<string,string>` | `{}` | Enables autofix from raw value to token |
|
|
305
333
|
| `ignoreValues` | `string[]` | CSS global keywords + `auto` | Skips keyword literals |
|
|
306
|
-
| `properties` | `Array<string|RegExp>` | built-in spacing patterns | Override targeted property set |
|
|
334
|
+
| `properties` | `Array<string|RegExp>` | built-in spacing patterns | Override targeted property set; string values must be supported spacing property names |
|
|
307
335
|
|
|
308
336
|
### `rhythmguard/no-offscale-transform`
|
|
309
337
|
|
|
@@ -325,7 +353,7 @@ Example:
|
|
|
325
353
|
|
|
326
354
|
Options:
|
|
327
355
|
|
|
328
|
-
`rhythmguard/no-offscale-transform` accepts the same scale options as `rhythmguard/use-scale`, but only for transform translation properties.
|
|
356
|
+
`rhythmguard/no-offscale-transform` accepts the same scale options as `rhythmguard/use-scale`, but only for transform translation properties. Its secondary options are also validated for unknown keys and invalid value shapes.
|
|
329
357
|
|
|
330
358
|
## Tailwind CSS Integration
|
|
331
359
|
|
|
@@ -441,8 +469,9 @@ Detailed methodology and custom args are documented in [`docs/BENCHMARKING.md`](
|
|
|
441
469
|
1. Create a GitHub release.
|
|
442
470
|
2. `release.yml` runs the Node/Stylelint matrix validation.
|
|
443
471
|
3. A tarball smoke test validates package exports and install behavior.
|
|
444
|
-
4.
|
|
445
|
-
5. `
|
|
472
|
+
4. If `NPM_TOKEN` is configured in repository secrets, the package is published to npm with provenance (`npm publish --provenance`).
|
|
473
|
+
5. If `NPM_TOKEN` is not configured, publish is skipped with an explicit workflow notice.
|
|
474
|
+
6. `post-publish-smoke.yml` verifies the published npm version can be installed and run in a clean project (and skips cleanly if the version is not on npm).
|
|
446
475
|
|
|
447
476
|
## Support and Bug Reports
|
|
448
477
|
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stylelint-plugin-rhythmguard",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Stylelint plugin for spacing scale and token enforcement",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"stylelint",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"stylelint": "^16.0.0"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
+
"known-css-properties": "^0.37.0",
|
|
64
65
|
"stylelint-config-tailwindcss": "^1.0.1"
|
|
65
66
|
},
|
|
66
67
|
"devDependencies": {
|
|
@@ -11,7 +11,10 @@ const {
|
|
|
11
11
|
parseLengthToken,
|
|
12
12
|
toPx,
|
|
13
13
|
} = require('../../utils/length');
|
|
14
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
buildScaleOptions,
|
|
16
|
+
validateNoOffscaleTransformSecondaryOptions,
|
|
17
|
+
} = require('../../utils/options');
|
|
15
18
|
const {
|
|
16
19
|
declarationValueIndex,
|
|
17
20
|
isMathFunction,
|
|
@@ -54,6 +57,15 @@ const ruleFunction = (primary, secondaryOptions) => {
|
|
|
54
57
|
return;
|
|
55
58
|
}
|
|
56
59
|
|
|
60
|
+
const validSecondaryOptions = validateNoOffscaleTransformSecondaryOptions(
|
|
61
|
+
result,
|
|
62
|
+
ruleName,
|
|
63
|
+
secondaryOptions,
|
|
64
|
+
);
|
|
65
|
+
if (!validSecondaryOptions) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
57
69
|
const options = buildScaleOptions(secondaryOptions);
|
|
58
70
|
if (options.invalidPreset) {
|
|
59
71
|
stylelint.utils.report({
|
|
@@ -8,7 +8,10 @@ const {
|
|
|
8
8
|
parseLengthToken,
|
|
9
9
|
toPx,
|
|
10
10
|
} = require('../../utils/length');
|
|
11
|
-
const {
|
|
11
|
+
const {
|
|
12
|
+
buildTokenOptions,
|
|
13
|
+
validatePreferTokenSecondaryOptions,
|
|
14
|
+
} = require('../../utils/options');
|
|
12
15
|
const {
|
|
13
16
|
createTokenRegex,
|
|
14
17
|
declarationValueIndex,
|
|
@@ -53,6 +56,15 @@ const ruleFunction = (primary, secondaryOptions) => {
|
|
|
53
56
|
return;
|
|
54
57
|
}
|
|
55
58
|
|
|
59
|
+
const validSecondaryOptions = validatePreferTokenSecondaryOptions(
|
|
60
|
+
result,
|
|
61
|
+
ruleName,
|
|
62
|
+
secondaryOptions,
|
|
63
|
+
);
|
|
64
|
+
if (!validSecondaryOptions) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
56
68
|
const options = buildTokenOptions(secondaryOptions);
|
|
57
69
|
if (options.invalidPreset) {
|
|
58
70
|
stylelint.utils.report({
|
|
@@ -11,7 +11,10 @@ const {
|
|
|
11
11
|
parseLengthToken,
|
|
12
12
|
toPx,
|
|
13
13
|
} = require('../../utils/length');
|
|
14
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
buildScaleOptions,
|
|
16
|
+
validateUseScaleSecondaryOptions,
|
|
17
|
+
} = require('../../utils/options');
|
|
15
18
|
const {
|
|
16
19
|
createTokenRegex,
|
|
17
20
|
declarationValueIndex,
|
|
@@ -122,6 +125,15 @@ const ruleFunction = (primary, secondaryOptions) => {
|
|
|
122
125
|
return;
|
|
123
126
|
}
|
|
124
127
|
|
|
128
|
+
const validSecondaryOptions = validateUseScaleSecondaryOptions(
|
|
129
|
+
result,
|
|
130
|
+
ruleName,
|
|
131
|
+
secondaryOptions,
|
|
132
|
+
);
|
|
133
|
+
if (!validSecondaryOptions) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
125
137
|
const options = buildScaleOptions(secondaryOptions);
|
|
126
138
|
if (options.invalidPreset) {
|
|
127
139
|
stylelint.utils.report({
|
package/src/utils/options.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const stylelint = require('stylelint');
|
|
4
|
+
const { all: knownCssProperties = [] } = require('known-css-properties');
|
|
3
5
|
const {
|
|
4
6
|
DEFAULT_IGNORE_KEYWORDS,
|
|
5
7
|
SPACING_PROPERTY_PATTERNS,
|
|
6
8
|
} = require('./constants');
|
|
9
|
+
const { parseLengthToken } = require('./length');
|
|
7
10
|
const {
|
|
8
11
|
getScalePreset,
|
|
9
12
|
listScalePresetNames,
|
|
@@ -11,6 +14,270 @@ const {
|
|
|
11
14
|
} = require('../presets/scales');
|
|
12
15
|
|
|
13
16
|
const DEFAULT_SCALE = getScalePreset('rhythmic-4') || [0, 4, 8, 12, 16, 24, 32];
|
|
17
|
+
const VALID_UNITS = new Set(['px', 'rem', 'em']);
|
|
18
|
+
const VALIDATE_ALWAYS = () => true;
|
|
19
|
+
|
|
20
|
+
const supportedSpacingProperties = new Set(
|
|
21
|
+
knownCssProperties.filter((property) =>
|
|
22
|
+
SPACING_PROPERTY_PATTERNS.some((pattern) => pattern.test(property)),
|
|
23
|
+
),
|
|
24
|
+
);
|
|
25
|
+
supportedSpacingProperties.add('translate-x');
|
|
26
|
+
supportedSpacingProperties.add('translate-y');
|
|
27
|
+
supportedSpacingProperties.add('translate-z');
|
|
28
|
+
|
|
29
|
+
function isPlainObject(value) {
|
|
30
|
+
return (
|
|
31
|
+
value !== null &&
|
|
32
|
+
typeof value === 'object' &&
|
|
33
|
+
!Array.isArray(value)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isBoolean(value) {
|
|
38
|
+
return typeof value === 'boolean';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isFinitePositiveNumber(value) {
|
|
42
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isNonEmptyString(value) {
|
|
46
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isSupportedUnit(value) {
|
|
50
|
+
if (!isNonEmptyString(value)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return VALID_UNITS.has(value.trim().toLowerCase());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isScaleEntry(value) {
|
|
58
|
+
if (typeof value === 'number') {
|
|
59
|
+
return Number.isFinite(value) && value >= 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!isNonEmptyString(value)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const parsed = parseLengthToken(value);
|
|
67
|
+
if (!parsed || parsed.number < 0) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return parsed.unit === '' || VALID_UNITS.has(parsed.unit);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isPropertyPatternEntry(value) {
|
|
75
|
+
if (value instanceof RegExp) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!isNonEmptyString(value)) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const normalized = value.trim().toLowerCase();
|
|
84
|
+
return supportedSpacingProperties.has(normalized);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isTokenMap(value) {
|
|
88
|
+
if (!isPlainObject(value)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return Object.values(value).every((tokenValue) => isNonEmptyString(tokenValue));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function validateSecondaryOptionShapes(result, ruleName, secondaryOptions, schema) {
|
|
96
|
+
if (secondaryOptions === undefined || secondaryOptions === null) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!isPlainObject(secondaryOptions)) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let valid = true;
|
|
105
|
+
|
|
106
|
+
for (const [optionName, descriptor] of Object.entries(schema)) {
|
|
107
|
+
const optionValue = secondaryOptions[optionName];
|
|
108
|
+
if (optionValue === undefined) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (descriptor.expectsArray && !Array.isArray(optionValue)) {
|
|
113
|
+
valid = false;
|
|
114
|
+
result.warn(
|
|
115
|
+
`Invalid value ${stringifyOptionValue(optionValue)} for option "${optionName}" of rule "${ruleName}"`,
|
|
116
|
+
{ stylelintType: 'invalidOption' },
|
|
117
|
+
);
|
|
118
|
+
result.stylelint.stylelintError = true;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (descriptor.expectsObject && !isPlainObject(optionValue)) {
|
|
123
|
+
valid = false;
|
|
124
|
+
result.warn(
|
|
125
|
+
`Invalid value ${stringifyOptionValue(optionValue)} for option "${optionName}" of rule "${ruleName}"`,
|
|
126
|
+
{ stylelintType: 'invalidOption' },
|
|
127
|
+
);
|
|
128
|
+
result.stylelint.stylelintError = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return valid;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function stringifyOptionValue(value) {
|
|
136
|
+
if (typeof value === 'string') {
|
|
137
|
+
return `"${value}"`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return `"${JSON.stringify(value)}"`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function buildPossibleOptionMap(schema) {
|
|
144
|
+
return Object.fromEntries(
|
|
145
|
+
Object.entries(schema).map(([optionName, descriptor]) => [
|
|
146
|
+
optionName,
|
|
147
|
+
[descriptor.entryValidator || VALIDATE_ALWAYS],
|
|
148
|
+
]),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function validateSecondaryOptions({
|
|
153
|
+
result,
|
|
154
|
+
ruleName,
|
|
155
|
+
secondaryOptions,
|
|
156
|
+
schema,
|
|
157
|
+
possibleOptionMap,
|
|
158
|
+
}) {
|
|
159
|
+
const validOptions = stylelint.utils.validateOptions(result, ruleName, {
|
|
160
|
+
actual: secondaryOptions,
|
|
161
|
+
optional: true,
|
|
162
|
+
possible: possibleOptionMap,
|
|
163
|
+
});
|
|
164
|
+
const validShapes = validateSecondaryOptionShapes(
|
|
165
|
+
result,
|
|
166
|
+
ruleName,
|
|
167
|
+
secondaryOptions,
|
|
168
|
+
schema,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
return validOptions && validShapes;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const SCALE_VALIDATION_SCHEMA = Object.freeze({
|
|
175
|
+
allowNegative: Object.freeze({
|
|
176
|
+
entryValidator: isBoolean,
|
|
177
|
+
}),
|
|
178
|
+
allowPercentages: Object.freeze({
|
|
179
|
+
entryValidator: isBoolean,
|
|
180
|
+
}),
|
|
181
|
+
baseFontSize: Object.freeze({
|
|
182
|
+
entryValidator: isFinitePositiveNumber,
|
|
183
|
+
}),
|
|
184
|
+
customScale: Object.freeze({
|
|
185
|
+
entryValidator: isScaleEntry,
|
|
186
|
+
expectsArray: true,
|
|
187
|
+
}),
|
|
188
|
+
enforceInsideMathFunctions: Object.freeze({
|
|
189
|
+
entryValidator: isBoolean,
|
|
190
|
+
}),
|
|
191
|
+
fixToScale: Object.freeze({
|
|
192
|
+
entryValidator: isBoolean,
|
|
193
|
+
}),
|
|
194
|
+
preset: Object.freeze({
|
|
195
|
+
entryValidator: isNonEmptyString,
|
|
196
|
+
}),
|
|
197
|
+
scale: Object.freeze({
|
|
198
|
+
entryValidator: isScaleEntry,
|
|
199
|
+
expectsArray: true,
|
|
200
|
+
}),
|
|
201
|
+
units: Object.freeze({
|
|
202
|
+
entryValidator: isSupportedUnit,
|
|
203
|
+
expectsArray: true,
|
|
204
|
+
}),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const USE_SCALE_VALIDATION_SCHEMA = Object.freeze({
|
|
208
|
+
...SCALE_VALIDATION_SCHEMA,
|
|
209
|
+
ignoreValues: Object.freeze({
|
|
210
|
+
entryValidator: isNonEmptyString,
|
|
211
|
+
expectsArray: true,
|
|
212
|
+
}),
|
|
213
|
+
properties: Object.freeze({
|
|
214
|
+
entryValidator: isPropertyPatternEntry,
|
|
215
|
+
expectsArray: true,
|
|
216
|
+
}),
|
|
217
|
+
tokenFunctions: Object.freeze({
|
|
218
|
+
entryValidator: isNonEmptyString,
|
|
219
|
+
expectsArray: true,
|
|
220
|
+
}),
|
|
221
|
+
tokenPattern: Object.freeze({
|
|
222
|
+
entryValidator: isNonEmptyString,
|
|
223
|
+
}),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const NO_OFFSCALE_TRANSFORM_VALIDATION_SCHEMA = Object.freeze({
|
|
227
|
+
...SCALE_VALIDATION_SCHEMA,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const PREFER_TOKEN_VALIDATION_SCHEMA = Object.freeze({
|
|
231
|
+
allowNumericScale: Object.freeze({
|
|
232
|
+
entryValidator: isBoolean,
|
|
233
|
+
}),
|
|
234
|
+
baseFontSize: Object.freeze({
|
|
235
|
+
entryValidator: isFinitePositiveNumber,
|
|
236
|
+
}),
|
|
237
|
+
customScale: Object.freeze({
|
|
238
|
+
entryValidator: isScaleEntry,
|
|
239
|
+
expectsArray: true,
|
|
240
|
+
}),
|
|
241
|
+
enforceInsideMathFunctions: Object.freeze({
|
|
242
|
+
entryValidator: isBoolean,
|
|
243
|
+
}),
|
|
244
|
+
ignoreValues: Object.freeze({
|
|
245
|
+
entryValidator: isNonEmptyString,
|
|
246
|
+
expectsArray: true,
|
|
247
|
+
}),
|
|
248
|
+
preset: Object.freeze({
|
|
249
|
+
entryValidator: isNonEmptyString,
|
|
250
|
+
}),
|
|
251
|
+
properties: Object.freeze({
|
|
252
|
+
entryValidator: isPropertyPatternEntry,
|
|
253
|
+
expectsArray: true,
|
|
254
|
+
}),
|
|
255
|
+
scale: Object.freeze({
|
|
256
|
+
entryValidator: isScaleEntry,
|
|
257
|
+
expectsArray: true,
|
|
258
|
+
}),
|
|
259
|
+
tokenFunctions: Object.freeze({
|
|
260
|
+
entryValidator: isNonEmptyString,
|
|
261
|
+
expectsArray: true,
|
|
262
|
+
}),
|
|
263
|
+
tokenMap: Object.freeze({
|
|
264
|
+
entryValidator: isTokenMap,
|
|
265
|
+
expectsObject: true,
|
|
266
|
+
}),
|
|
267
|
+
tokenPattern: Object.freeze({
|
|
268
|
+
entryValidator: isNonEmptyString,
|
|
269
|
+
}),
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const USE_SCALE_POSSIBLE_OPTIONS = Object.freeze(
|
|
273
|
+
buildPossibleOptionMap(USE_SCALE_VALIDATION_SCHEMA),
|
|
274
|
+
);
|
|
275
|
+
const NO_OFFSCALE_TRANSFORM_POSSIBLE_OPTIONS = Object.freeze(
|
|
276
|
+
buildPossibleOptionMap(NO_OFFSCALE_TRANSFORM_VALIDATION_SCHEMA),
|
|
277
|
+
);
|
|
278
|
+
const PREFER_TOKEN_POSSIBLE_OPTIONS = Object.freeze(
|
|
279
|
+
buildPossibleOptionMap(PREFER_TOKEN_VALIDATION_SCHEMA),
|
|
280
|
+
);
|
|
14
281
|
|
|
15
282
|
function buildScaleOptions(rawOptions) {
|
|
16
283
|
const options = rawOptions || {};
|
|
@@ -87,7 +354,40 @@ function buildTokenOptions(rawOptions) {
|
|
|
87
354
|
};
|
|
88
355
|
}
|
|
89
356
|
|
|
357
|
+
function validateUseScaleSecondaryOptions(result, ruleName, secondaryOptions) {
|
|
358
|
+
return validateSecondaryOptions({
|
|
359
|
+
result,
|
|
360
|
+
ruleName,
|
|
361
|
+
secondaryOptions,
|
|
362
|
+
schema: USE_SCALE_VALIDATION_SCHEMA,
|
|
363
|
+
possibleOptionMap: USE_SCALE_POSSIBLE_OPTIONS,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function validateNoOffscaleTransformSecondaryOptions(result, ruleName, secondaryOptions) {
|
|
368
|
+
return validateSecondaryOptions({
|
|
369
|
+
result,
|
|
370
|
+
ruleName,
|
|
371
|
+
secondaryOptions,
|
|
372
|
+
schema: NO_OFFSCALE_TRANSFORM_VALIDATION_SCHEMA,
|
|
373
|
+
possibleOptionMap: NO_OFFSCALE_TRANSFORM_POSSIBLE_OPTIONS,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function validatePreferTokenSecondaryOptions(result, ruleName, secondaryOptions) {
|
|
378
|
+
return validateSecondaryOptions({
|
|
379
|
+
result,
|
|
380
|
+
ruleName,
|
|
381
|
+
secondaryOptions,
|
|
382
|
+
schema: PREFER_TOKEN_VALIDATION_SCHEMA,
|
|
383
|
+
possibleOptionMap: PREFER_TOKEN_POSSIBLE_OPTIONS,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
90
387
|
module.exports = {
|
|
91
388
|
buildScaleOptions,
|
|
92
389
|
buildTokenOptions,
|
|
390
|
+
validateNoOffscaleTransformSecondaryOptions,
|
|
391
|
+
validatePreferTokenSecondaryOptions,
|
|
392
|
+
validateUseScaleSecondaryOptions,
|
|
93
393
|
};
|