stylelint-plugin-rhythmguard 0.1.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 +22 -0
- package/LICENSE +21 -0
- package/README.md +349 -0
- package/assets/rhythmguard-banner.svg +42 -0
- package/assets/rhythmguard-rules.svg +45 -0
- package/package.json +68 -0
- package/src/configs/recommended.js +13 -0
- package/src/configs/strict.js +25 -0
- package/src/index.js +19 -0
- package/src/presets/index.js +13 -0
- package/src/presets/scales.js +122 -0
- package/src/rules/no-offscale-transform/index.js +169 -0
- package/src/rules/prefer-token/index.js +188 -0
- package/src/rules/use-scale/index.js +246 -0
- package/src/utils/constants.js +41 -0
- package/src/utils/length.js +139 -0
- package/src/utils/options.js +92 -0
- package/src/utils/value-utils.js +120 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
plugins: ['stylelint-plugin-rhythmguard'],
|
|
5
|
+
rules: {
|
|
6
|
+
'rhythmguard/use-scale': [
|
|
7
|
+
true,
|
|
8
|
+
{
|
|
9
|
+
scale: [0, 4, 8, 12, 16, 24, 32, 40, 48, 64],
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
'rhythmguard/no-offscale-transform': [
|
|
13
|
+
true,
|
|
14
|
+
{
|
|
15
|
+
scale: [0, 4, 8, 12, 16, 24, 32],
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
'rhythmguard/prefer-token': [
|
|
19
|
+
true,
|
|
20
|
+
{
|
|
21
|
+
tokenPattern: '^--space-',
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const useScale = require('./rules/use-scale');
|
|
4
|
+
const preferToken = require('./rules/prefer-token');
|
|
5
|
+
const noOffscaleTransform = require('./rules/no-offscale-transform');
|
|
6
|
+
|
|
7
|
+
const rules = [useScale, preferToken, noOffscaleTransform];
|
|
8
|
+
|
|
9
|
+
module.exports = rules;
|
|
10
|
+
module.exports.rules = {
|
|
11
|
+
[useScale.ruleName]: useScale,
|
|
12
|
+
[preferToken.ruleName]: preferToken,
|
|
13
|
+
[noOffscaleTransform.ruleName]: noOffscaleTransform,
|
|
14
|
+
};
|
|
15
|
+
module.exports.configs = {
|
|
16
|
+
recommended: require('./configs/recommended'),
|
|
17
|
+
strict: require('./configs/strict'),
|
|
18
|
+
};
|
|
19
|
+
module.exports.presets = require('./presets');
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function createModularScale({ base, ratio, steps }) {
|
|
4
|
+
const values = [0];
|
|
5
|
+
let current = base;
|
|
6
|
+
|
|
7
|
+
for (let i = 0; i < steps; i += 1) {
|
|
8
|
+
values.push(Math.round(current));
|
|
9
|
+
current *= ratio;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return [...new Set(values)].sort((a, b) => a - b);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const SCALE_PRESETS = Object.freeze({
|
|
16
|
+
'rhythmic-4': Object.freeze([0, 4, 8, 12, 16, 24, 32, 40, 48, 64]),
|
|
17
|
+
'rhythmic-8': Object.freeze([0, 8, 16, 24, 32, 40, 48, 64, 80, 96]),
|
|
18
|
+
'product-material-8dp': Object.freeze([0, 4, 8, 12, 16, 24, 32, 40, 48, 56, 64, 72, 80]),
|
|
19
|
+
'product-atlassian-8px': Object.freeze([0, 2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 64, 80]),
|
|
20
|
+
'product-carbon-2x': Object.freeze([0, 2, 4, 8, 12, 16, 24, 32, 40, 48, 64, 80]),
|
|
21
|
+
'editorial-baseline-4': Object.freeze([0, 4, 8, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64]),
|
|
22
|
+
'editorial-baseline-6': Object.freeze([0, 6, 12, 18, 24, 30, 36, 48, 60, 72]),
|
|
23
|
+
compact: Object.freeze([0, 2, 4, 6, 8, 12, 16, 20, 24, 32]),
|
|
24
|
+
fibonacci: Object.freeze([0, 2, 3, 5, 8, 13, 21, 34, 55, 89]),
|
|
25
|
+
'powers-of-two': Object.freeze([0, 2, 4, 8, 16, 32, 64, 128]),
|
|
26
|
+
'golden-ratio': Object.freeze(createModularScale({ base: 4, ratio: 1.61803398875, steps: 10 })),
|
|
27
|
+
'modular-major-second': Object.freeze(createModularScale({ base: 8, ratio: 1.125, steps: 12 })),
|
|
28
|
+
'modular-minor-third': Object.freeze(createModularScale({ base: 4, ratio: 1.2, steps: 12 })),
|
|
29
|
+
'modular-major-third': Object.freeze(createModularScale({ base: 4, ratio: 1.25, steps: 12 })),
|
|
30
|
+
'modular-augmented-fourth': Object.freeze(createModularScale({ base: 4, ratio: 1.41421356237, steps: 12 })),
|
|
31
|
+
'modular-perfect-fourth': Object.freeze(createModularScale({ base: 4, ratio: 4 / 3, steps: 12 })),
|
|
32
|
+
'modular-perfect-fifth': Object.freeze(createModularScale({ base: 4, ratio: 1.5, steps: 12 })),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const PRESET_ALIASES = Object.freeze({
|
|
36
|
+
'4pt': 'rhythmic-4',
|
|
37
|
+
'8pt': 'rhythmic-8',
|
|
38
|
+
'atlassian-8': 'product-atlassian-8px',
|
|
39
|
+
carbon: 'product-carbon-2x',
|
|
40
|
+
material: 'product-material-8dp',
|
|
41
|
+
'baseline-4': 'editorial-baseline-4',
|
|
42
|
+
'baseline-6': 'editorial-baseline-6',
|
|
43
|
+
golden: 'golden-ratio',
|
|
44
|
+
'major-second': 'modular-major-second',
|
|
45
|
+
'major-third': 'modular-major-third',
|
|
46
|
+
'minor-third': 'modular-minor-third',
|
|
47
|
+
'augmented-fourth': 'modular-augmented-fourth',
|
|
48
|
+
'perfect-fifth': 'modular-perfect-fifth',
|
|
49
|
+
'perfect-fourth': 'modular-perfect-fourth',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
function normalizePresetName(name) {
|
|
53
|
+
return String(name).trim().toLowerCase();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolvePresetName(name) {
|
|
57
|
+
if (typeof name !== 'string' || name.trim().length === 0) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const normalized = normalizePresetName(name);
|
|
62
|
+
return PRESET_ALIASES[normalized] || normalized;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getScalePreset(name) {
|
|
66
|
+
const resolvedName = resolvePresetName(name);
|
|
67
|
+
if (!resolvedName) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return SCALE_PRESETS[resolvedName] || null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function listScalePresetNames() {
|
|
75
|
+
return Object.keys(SCALE_PRESETS);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function resolveScaleSelection(options, defaultScale) {
|
|
79
|
+
const hasScale = Array.isArray(options.scale);
|
|
80
|
+
const hasCustomScale = Array.isArray(options.customScale);
|
|
81
|
+
const presetName = resolvePresetName(options.preset);
|
|
82
|
+
|
|
83
|
+
let invalidPreset = null;
|
|
84
|
+
let selectedPreset = null;
|
|
85
|
+
let scale = defaultScale;
|
|
86
|
+
|
|
87
|
+
if (presetName) {
|
|
88
|
+
const presetScale = getScalePreset(presetName);
|
|
89
|
+
|
|
90
|
+
if (presetScale) {
|
|
91
|
+
selectedPreset = presetName;
|
|
92
|
+
scale = presetScale;
|
|
93
|
+
} else {
|
|
94
|
+
invalidPreset = String(options.preset);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (hasScale) {
|
|
99
|
+
scale = options.scale;
|
|
100
|
+
selectedPreset = null;
|
|
101
|
+
invalidPreset = null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (hasCustomScale) {
|
|
105
|
+
scale = options.customScale;
|
|
106
|
+
selectedPreset = null;
|
|
107
|
+
invalidPreset = null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
invalidPreset,
|
|
112
|
+
scale,
|
|
113
|
+
selectedPreset,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
SCALE_PRESETS,
|
|
119
|
+
getScalePreset,
|
|
120
|
+
listScalePresetNames,
|
|
121
|
+
resolveScaleSelection,
|
|
122
|
+
};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const stylelint = require('stylelint');
|
|
4
|
+
const valueParser = require('postcss-value-parser');
|
|
5
|
+
const {
|
|
6
|
+
formatLength,
|
|
7
|
+
fromPx,
|
|
8
|
+
nearestScaleValues,
|
|
9
|
+
normalizeScale,
|
|
10
|
+
numbersEqual,
|
|
11
|
+
parseLengthToken,
|
|
12
|
+
toPx,
|
|
13
|
+
} = require('../../utils/length');
|
|
14
|
+
const { buildScaleOptions } = require('../../utils/options');
|
|
15
|
+
const {
|
|
16
|
+
declarationValueIndex,
|
|
17
|
+
walkTransformTranslateNodes,
|
|
18
|
+
} = require('../../utils/value-utils');
|
|
19
|
+
|
|
20
|
+
const ruleName = 'rhythmguard/no-offscale-transform';
|
|
21
|
+
const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
22
|
+
invalidPreset: (presetName, presetNames) =>
|
|
23
|
+
`Unknown scale preset "${presetName}". Available presets: ${presetNames.join(', ')}.`,
|
|
24
|
+
rejected: (value, lower, upper) =>
|
|
25
|
+
`Unexpected transform translation value "${value}". Use scale values (nearest: ${lower} or ${upper}).`,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function getFixedNodeValue(parsedLength, nearestPx, options) {
|
|
29
|
+
const unit = parsedLength.unit || 'px';
|
|
30
|
+
if (unit === '%' || !options.units.includes(unit)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const signedNearest = parsedLength.number < 0 ? -Math.abs(nearestPx) : nearestPx;
|
|
35
|
+
const converted = fromPx(signedNearest, unit, options.baseFontSize);
|
|
36
|
+
|
|
37
|
+
if (converted === null) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return formatLength(converted, unit);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ruleFunction = (primary, secondaryOptions) => {
|
|
45
|
+
return (root, result) => {
|
|
46
|
+
const valid = stylelint.utils.validateOptions(result, ruleName, {
|
|
47
|
+
actual: primary,
|
|
48
|
+
possible: [true],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (!valid) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const options = buildScaleOptions(secondaryOptions);
|
|
56
|
+
if (options.invalidPreset) {
|
|
57
|
+
stylelint.utils.report({
|
|
58
|
+
message: messages.invalidPreset(options.invalidPreset, options.presetNames),
|
|
59
|
+
node: root,
|
|
60
|
+
result,
|
|
61
|
+
ruleName,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const scalePx = normalizeScale(options.scale, options.baseFontSize);
|
|
66
|
+
|
|
67
|
+
root.walkDecls((decl) => {
|
|
68
|
+
const prop = decl.prop.toLowerCase();
|
|
69
|
+
if (prop !== 'transform' && prop !== 'translate' && !prop.startsWith('translate-')) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const parsed = valueParser(decl.value);
|
|
74
|
+
let changed = false;
|
|
75
|
+
|
|
76
|
+
const report = (node, nearest, fixedValue = null) => {
|
|
77
|
+
const index = declarationValueIndex(decl) + node.sourceIndex;
|
|
78
|
+
const endIndex = index + node.value.length;
|
|
79
|
+
|
|
80
|
+
const payload = {
|
|
81
|
+
endIndex,
|
|
82
|
+
index,
|
|
83
|
+
message: messages.rejected(
|
|
84
|
+
node.value,
|
|
85
|
+
formatLength(nearest.lower, 'px'),
|
|
86
|
+
formatLength(nearest.upper, 'px'),
|
|
87
|
+
),
|
|
88
|
+
node: decl,
|
|
89
|
+
result,
|
|
90
|
+
ruleName,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (fixedValue) {
|
|
94
|
+
payload.fix = () => {
|
|
95
|
+
node.value = fixedValue;
|
|
96
|
+
return true;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
stylelint.utils.report(payload);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const checkNode = (node) => {
|
|
104
|
+
const parsedLength = parseLengthToken(node.value);
|
|
105
|
+
if (!parsedLength || parsedLength.number === 0) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (parsedLength.unit === '%' && options.allowPercentages) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!options.allowNegative && parsedLength.number < 0) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const pxValue = toPx(Math.abs(parsedLength.number), parsedLength.unit, options.baseFontSize);
|
|
118
|
+
if (pxValue === null) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const isOnScale = scalePx.some((entry) => numbersEqual(entry, pxValue));
|
|
123
|
+
if (isOnScale) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const nearest = nearestScaleValues(pxValue, scalePx);
|
|
128
|
+
if (!nearest) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const fixedValue = options.fixToScale
|
|
133
|
+
? getFixedNodeValue(parsedLength, nearest.nearest, options)
|
|
134
|
+
: null;
|
|
135
|
+
|
|
136
|
+
report(node, nearest, fixedValue);
|
|
137
|
+
changed = true;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
if (prop === 'transform') {
|
|
141
|
+
walkTransformTranslateNodes(parsed, (node) => {
|
|
142
|
+
checkNode(node);
|
|
143
|
+
});
|
|
144
|
+
} else {
|
|
145
|
+
for (const node of parsed.nodes) {
|
|
146
|
+
if (node.type === 'word') {
|
|
147
|
+
checkNode(node);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (changed) {
|
|
153
|
+
decl.value = parsed.toString();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
ruleFunction.ruleName = ruleName;
|
|
160
|
+
ruleFunction.messages = messages;
|
|
161
|
+
ruleFunction.meta = {
|
|
162
|
+
fixable: true,
|
|
163
|
+
url: 'https://github.com/petrilahdelma/stylelint-plugin-rhythmguard#rhythmguardno-offscale-transform',
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
module.exports = stylelint.createPlugin(ruleName, ruleFunction);
|
|
167
|
+
module.exports.ruleName = ruleName;
|
|
168
|
+
module.exports.messages = messages;
|
|
169
|
+
module.exports.meta = ruleFunction.meta;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const stylelint = require('stylelint');
|
|
4
|
+
const valueParser = require('postcss-value-parser');
|
|
5
|
+
const {
|
|
6
|
+
normalizeScale,
|
|
7
|
+
numbersEqual,
|
|
8
|
+
parseLengthToken,
|
|
9
|
+
toPx,
|
|
10
|
+
} = require('../../utils/length');
|
|
11
|
+
const { buildTokenOptions } = require('../../utils/options');
|
|
12
|
+
const {
|
|
13
|
+
createTokenRegex,
|
|
14
|
+
declarationValueIndex,
|
|
15
|
+
isKeyword,
|
|
16
|
+
isMathFunction,
|
|
17
|
+
isTokenFunction,
|
|
18
|
+
propertyMatches,
|
|
19
|
+
walkRootValueNodes,
|
|
20
|
+
walkTransformTranslateNodes,
|
|
21
|
+
} = require('../../utils/value-utils');
|
|
22
|
+
|
|
23
|
+
const ruleName = 'rhythmguard/prefer-token';
|
|
24
|
+
|
|
25
|
+
const messages = stylelint.utils.ruleMessages(ruleName, {
|
|
26
|
+
invalidPreset: (presetName, presetNames) =>
|
|
27
|
+
`Unknown scale preset "${presetName}". Available presets: ${presetNames.join(', ')}.`,
|
|
28
|
+
rejected: (value) =>
|
|
29
|
+
`Unexpected raw spacing value "${value}". Use design tokens for spacing decisions.`,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function resolveTokenReplacement(tokenMap, raw, normalizedPx) {
|
|
33
|
+
if (Object.prototype.hasOwnProperty.call(tokenMap, raw)) {
|
|
34
|
+
return tokenMap[raw];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const pxKey = `${normalizedPx}px`;
|
|
38
|
+
if (Object.prototype.hasOwnProperty.call(tokenMap, pxKey)) {
|
|
39
|
+
return tokenMap[pxKey];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const ruleFunction = (primary, secondaryOptions) => {
|
|
46
|
+
return (root, result) => {
|
|
47
|
+
const valid = stylelint.utils.validateOptions(result, ruleName, {
|
|
48
|
+
actual: primary,
|
|
49
|
+
possible: [true],
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!valid) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const options = buildTokenOptions(secondaryOptions);
|
|
57
|
+
if (options.invalidPreset) {
|
|
58
|
+
stylelint.utils.report({
|
|
59
|
+
message: messages.invalidPreset(options.invalidPreset, options.presetNames),
|
|
60
|
+
node: root,
|
|
61
|
+
result,
|
|
62
|
+
ruleName,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const tokenRegex = createTokenRegex(options.tokenPattern, result, ruleName);
|
|
67
|
+
const scalePx = normalizeScale(options.scale, options.baseFontSize);
|
|
68
|
+
|
|
69
|
+
root.walkDecls((decl) => {
|
|
70
|
+
const prop = decl.prop.toLowerCase();
|
|
71
|
+
if (prop.startsWith('--')) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!propertyMatches(prop, options.properties)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const parsed = valueParser(decl.value);
|
|
80
|
+
let changed = false;
|
|
81
|
+
|
|
82
|
+
const reportNode = (node, replacement = null) => {
|
|
83
|
+
const index = declarationValueIndex(decl) + node.sourceIndex;
|
|
84
|
+
const endIndex = index + node.value.length;
|
|
85
|
+
|
|
86
|
+
const payload = {
|
|
87
|
+
endIndex,
|
|
88
|
+
index,
|
|
89
|
+
message: messages.rejected(node.value),
|
|
90
|
+
node: decl,
|
|
91
|
+
result,
|
|
92
|
+
ruleName,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
if (replacement) {
|
|
96
|
+
payload.fix = () => {
|
|
97
|
+
node.value = replacement;
|
|
98
|
+
return true;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
stylelint.utils.report(payload);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const checkWordNode = (node, parentFunctionName) => {
|
|
106
|
+
if (isKeyword(node.value, options.ignoreValues)) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
parentFunctionName &&
|
|
112
|
+
isMathFunction(parentFunctionName)
|
|
113
|
+
) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const parsedLength = parseLengthToken(node.value);
|
|
118
|
+
if (!parsedLength) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (parsedLength.number === 0) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const absPx = toPx(Math.abs(parsedLength.number), parsedLength.unit, options.baseFontSize);
|
|
127
|
+
if (absPx === null) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (options.allowNumericScale) {
|
|
132
|
+
const onScale = scalePx.some((entry) => numbersEqual(entry, absPx));
|
|
133
|
+
if (onScale) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const replacement = resolveTokenReplacement(options.tokenMap, node.value, absPx);
|
|
139
|
+
|
|
140
|
+
reportNode(node, replacement);
|
|
141
|
+
return true;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
if (prop === 'transform') {
|
|
145
|
+
walkTransformTranslateNodes(parsed, (node) => {
|
|
146
|
+
changed = checkWordNode(node) || changed;
|
|
147
|
+
});
|
|
148
|
+
} else {
|
|
149
|
+
walkRootValueNodes(parsed, (node, parentFunctionName) => {
|
|
150
|
+
if (node.type === 'function') {
|
|
151
|
+
if (isTokenFunction(node, options.tokenFunctions, tokenRegex)) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (isMathFunction(node.value)) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (node.type !== 'word') {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
changed = checkWordNode(node, parentFunctionName) || changed;
|
|
167
|
+
return false;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (changed) {
|
|
172
|
+
decl.value = parsed.toString();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
ruleFunction.ruleName = ruleName;
|
|
179
|
+
ruleFunction.messages = messages;
|
|
180
|
+
ruleFunction.meta = {
|
|
181
|
+
fixable: true,
|
|
182
|
+
url: 'https://github.com/petrilahdelma/stylelint-plugin-rhythmguard#rhythmguardprefer-token',
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
module.exports = stylelint.createPlugin(ruleName, ruleFunction);
|
|
186
|
+
module.exports.ruleName = ruleName;
|
|
187
|
+
module.exports.messages = messages;
|
|
188
|
+
module.exports.meta = ruleFunction.meta;
|