@dialpad/stylelint-plugin-dialtone 1.4.0-next.1 → 1.4.0-next.2
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.
|
@@ -72,6 +72,27 @@ const VALUE_MAP = {
|
|
|
72
72
|
},
|
|
73
73
|
};
|
|
74
74
|
|
|
75
|
+
// Physical → logical utility class prefix mappings
|
|
76
|
+
// These map directional class names to their logical equivalents in templates.
|
|
77
|
+
// Sorted by longest prefix first to avoid partial matches (d-ml- before d-l-).
|
|
78
|
+
const CLASS_PREFIX_MAP = [
|
|
79
|
+
// Margin
|
|
80
|
+
['d-mt-', 'd-mbs-'],
|
|
81
|
+
['d-mr-', 'd-mie-'],
|
|
82
|
+
['d-mb-', 'd-mbe-'],
|
|
83
|
+
['d-ml-', 'd-mis-'],
|
|
84
|
+
// Padding
|
|
85
|
+
['d-pt-', 'd-pbs-'],
|
|
86
|
+
['d-pr-', 'd-pie-'],
|
|
87
|
+
['d-pb-', 'd-pbe-'],
|
|
88
|
+
['d-pl-', 'd-pis-'],
|
|
89
|
+
// Position (inset)
|
|
90
|
+
['d-t-', 'd-ibs-'],
|
|
91
|
+
['d-r-', 'd-iie-'],
|
|
92
|
+
['d-b-', 'd-ibe-'],
|
|
93
|
+
['d-l-', 'd-iis-'],
|
|
94
|
+
];
|
|
95
|
+
|
|
75
96
|
function escapeRegex(string) {
|
|
76
97
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
77
98
|
}
|
|
@@ -117,6 +138,47 @@ function fixLogicalProperties(content) {
|
|
|
117
138
|
return fixedLines.join('\n');
|
|
118
139
|
}
|
|
119
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Replace physical utility class prefixes with logical equivalents in a string.
|
|
143
|
+
* e.g. "d-pt-100 d-ml-200" → "d-pbs-100 d-mis-200"
|
|
144
|
+
*/
|
|
145
|
+
function fixLogicalClassNames(content) {
|
|
146
|
+
let fixed = content;
|
|
147
|
+
for (const [physical, logical] of CLASS_PREFIX_MAP) {
|
|
148
|
+
// Match the physical prefix followed by a token stop number or 'n' + number (negative)
|
|
149
|
+
// Word boundary ensures we don't match partial class names
|
|
150
|
+
const regex = new RegExp(escapeRegex(physical) + '(n?[0-9]+)', 'g');
|
|
151
|
+
fixed = fixed.replace(regex, `${logical}$1`);
|
|
152
|
+
}
|
|
153
|
+
return fixed;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Process class="..." and :class="..." attributes in template content,
|
|
158
|
+
* replacing physical class prefixes with logical equivalents.
|
|
159
|
+
*/
|
|
160
|
+
function processTemplateClassNames(content) {
|
|
161
|
+
let hasChanges = false;
|
|
162
|
+
|
|
163
|
+
// Match class="..." attributes (static)
|
|
164
|
+
const staticClassRegex = /(class=")(.*?)(")/g;
|
|
165
|
+
let fixed = content.replace(staticClassRegex, (_match, open, classes, close) => {
|
|
166
|
+
const fixedClasses = fixLogicalClassNames(classes);
|
|
167
|
+
if (fixedClasses !== classes) hasChanges = true;
|
|
168
|
+
return open + fixedClasses + close;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Match :class="'...'" attributes (dynamic with string literal)
|
|
172
|
+
const dynamicClassRegex = /(:class="')(.*?)(')/g;
|
|
173
|
+
fixed = fixed.replace(dynamicClassRegex, (_match, open, classes, close) => {
|
|
174
|
+
const fixedClasses = fixLogicalClassNames(classes);
|
|
175
|
+
if (fixedClasses !== classes) hasChanges = true;
|
|
176
|
+
return open + fixedClasses + close;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return { fixed, hasChanges };
|
|
180
|
+
}
|
|
181
|
+
|
|
120
182
|
function processStyleBlocks(content) {
|
|
121
183
|
// Match all <style> blocks (with any attributes like lang, scoped, etc.)
|
|
122
184
|
const styleRegex = /(<style[^>]*>)([\s\S]*?)(<\/style>)/gi;
|
|
@@ -150,11 +212,12 @@ function processMarkdownFile(filePath) {
|
|
|
150
212
|
return part; // Keep code blocks unchanged
|
|
151
213
|
}
|
|
152
214
|
// Process style blocks in non-code parts
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
215
|
+
const styleResult = processStyleBlocks(part);
|
|
216
|
+
if (styleResult.hasChanges) hasChanges = true;
|
|
217
|
+
// Process class names in non-code parts
|
|
218
|
+
const classResult = processTemplateClassNames(styleResult.fixed);
|
|
219
|
+
if (classResult.hasChanges) hasChanges = true;
|
|
220
|
+
return classResult.fixed;
|
|
158
221
|
});
|
|
159
222
|
|
|
160
223
|
if (hasChanges) {
|
|
@@ -171,11 +234,21 @@ function processMarkdownFile(filePath) {
|
|
|
171
234
|
|
|
172
235
|
function processFileWithStyleBlocks(filePath) {
|
|
173
236
|
try {
|
|
174
|
-
|
|
175
|
-
|
|
237
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
238
|
+
let hasChanges = false;
|
|
239
|
+
|
|
240
|
+
// Fix CSS properties in <style> blocks
|
|
241
|
+
const styleResult = processStyleBlocks(content);
|
|
242
|
+
if (styleResult.hasChanges) hasChanges = true;
|
|
243
|
+
content = styleResult.fixed;
|
|
176
244
|
|
|
177
|
-
|
|
178
|
-
|
|
245
|
+
// Fix class names in templates (outside <style> blocks)
|
|
246
|
+
const classResult = processTemplateClassNames(content);
|
|
247
|
+
if (classResult.hasChanges) hasChanges = true;
|
|
248
|
+
content = classResult.fixed;
|
|
249
|
+
|
|
250
|
+
if (hasChanges) {
|
|
251
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
179
252
|
console.log(`Fixed: ${filePath}`);
|
|
180
253
|
return true;
|
|
181
254
|
}
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const noBaseColorTokens = require('./rules/no-base-color-tokens');
|
|
4
|
+
const noDeprecatedSizeTokens = require('./rules/no-deprecated-size-tokens');
|
|
4
5
|
const noDeprecatedSpaceTokens = require('./rules/no-deprecated-space-tokens');
|
|
5
6
|
const noMixins = require('./rules/no-mixins');
|
|
6
7
|
const recommendFontStyleTokens = require('./rules/recommend-font-style-tokens');
|
|
@@ -9,6 +10,7 @@ const useLogical = require('stylelint-use-logical');
|
|
|
9
10
|
|
|
10
11
|
module.exports = [
|
|
11
12
|
noBaseColorTokens,
|
|
13
|
+
noDeprecatedSizeTokens,
|
|
12
14
|
noDeprecatedSpaceTokens,
|
|
13
15
|
noMixins,
|
|
14
16
|
recommendFontStyleTokens,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const stylelint = require('stylelint');
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
createPlugin,
|
|
5
|
+
utils: { report, ruleMessages, validateOptions },
|
|
6
|
+
} = stylelint;
|
|
7
|
+
|
|
8
|
+
const ruleName = '@dialpad/stylelint-plugin-dialtone/no-deprecated-size-tokens';
|
|
9
|
+
|
|
10
|
+
const messages = ruleMessages(ruleName, {
|
|
11
|
+
deprecatedSizeToken:
|
|
12
|
+
'--dt-size-* tokens have been replaced. Use --dt-layout-* for layout (widths/heights) or --dt-spacing-* for spacing (padding/margin). Run "npx dialtone-migration-helper" and select "size-to-layout".',
|
|
13
|
+
deprecatedSpaceToken:
|
|
14
|
+
'--dt-space-* tokens have been replaced by --dt-spacing-*. Run "npx dialtone-migration-helper" and select "space-to-spacing".',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const meta = {
|
|
18
|
+
url: 'https://github.com/dialpad/dialtone/blob/staging/packages/stylelint-plugin-dialtone/docs/rules/no-deprecated-size-tokens.md',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** @type {import('stylelint').Rule} */
|
|
22
|
+
const ruleFunction = (primary) => {
|
|
23
|
+
return (root, result) => {
|
|
24
|
+
const validOptions = validateOptions(result, ruleName, {
|
|
25
|
+
actual: primary,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!validOptions) return;
|
|
29
|
+
|
|
30
|
+
root.walkDecls((declaration) => {
|
|
31
|
+
// Match --dt-size-{number} but NOT --dt-size-border-* or --dt-size-radius-* (those are valid)
|
|
32
|
+
const sizeTokenMatches = declaration.value.match(/var\(--dt-size-(?!border-|radius-)[^)]+\)/g);
|
|
33
|
+
if (sizeTokenMatches) {
|
|
34
|
+
sizeTokenMatches.forEach(() => {
|
|
35
|
+
report({
|
|
36
|
+
result,
|
|
37
|
+
ruleName,
|
|
38
|
+
node: declaration,
|
|
39
|
+
message: messages.deprecatedSizeToken,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const spaceTokenMatches = declaration.value.match(/var\(--dt-space-[^)]+\)/g);
|
|
45
|
+
if (spaceTokenMatches) {
|
|
46
|
+
spaceTokenMatches.forEach(() => {
|
|
47
|
+
report({
|
|
48
|
+
result,
|
|
49
|
+
ruleName,
|
|
50
|
+
node: declaration,
|
|
51
|
+
message: messages.deprecatedSpaceToken,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
ruleFunction.ruleName = ruleName;
|
|
60
|
+
ruleFunction.messages = messages;
|
|
61
|
+
ruleFunction.meta = meta;
|
|
62
|
+
|
|
63
|
+
module.exports = createPlugin(ruleName, ruleFunction);
|