@dialpad/stylelint-plugin-dialtone 1.3.1-next.1 → 1.4.0-next.1

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.
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+
6
+ // Physical to logical property mappings
7
+ const PROPERTY_MAP = {
8
+ // Margins
9
+ 'margin-left': 'margin-inline-start',
10
+ 'margin-right': 'margin-inline-end',
11
+ 'margin-top': 'margin-block-start',
12
+ 'margin-bottom': 'margin-block-end',
13
+
14
+ // Padding
15
+ 'padding-left': 'padding-inline-start',
16
+ 'padding-right': 'padding-inline-end',
17
+ 'padding-top': 'padding-block-start',
18
+ 'padding-bottom': 'padding-block-end',
19
+
20
+ // Positioning (inset)
21
+ 'left': 'inset-inline-start',
22
+ 'right': 'inset-inline-end',
23
+ 'top': 'inset-block-start',
24
+ 'bottom': 'inset-block-end',
25
+
26
+ // Border
27
+ 'border-left': 'border-inline-start',
28
+ 'border-right': 'border-inline-end',
29
+ 'border-top': 'border-block-start',
30
+ 'border-bottom': 'border-block-end',
31
+ 'border-left-width': 'border-inline-start-width',
32
+ 'border-right-width': 'border-inline-end-width',
33
+ 'border-top-width': 'border-block-start-width',
34
+ 'border-bottom-width': 'border-block-end-width',
35
+ 'border-left-style': 'border-inline-start-style',
36
+ 'border-right-style': 'border-inline-end-style',
37
+ 'border-top-style': 'border-block-start-style',
38
+ 'border-bottom-style': 'border-block-end-style',
39
+ 'border-left-color': 'border-inline-start-color',
40
+ 'border-right-color': 'border-inline-end-color',
41
+ 'border-top-color': 'border-block-start-color',
42
+ 'border-bottom-color': 'border-block-end-color',
43
+
44
+ // Border radius
45
+ 'border-top-left-radius': 'border-start-start-radius',
46
+ 'border-top-right-radius': 'border-start-end-radius',
47
+ 'border-bottom-left-radius': 'border-end-start-radius',
48
+ 'border-bottom-right-radius': 'border-end-end-radius',
49
+
50
+ // Sizing
51
+ 'width': 'inline-size',
52
+ 'height': 'block-size',
53
+ 'min-width': 'min-inline-size',
54
+ 'max-width': 'max-inline-size',
55
+ 'min-height': 'min-block-size',
56
+ 'max-height': 'max-block-size',
57
+ };
58
+
59
+ // Value mappings for specific properties
60
+ const VALUE_MAP = {
61
+ 'text-align': {
62
+ 'left': 'start',
63
+ 'right': 'end',
64
+ },
65
+ 'float': {
66
+ 'left': 'inline-start',
67
+ 'right': 'inline-end',
68
+ },
69
+ 'clear': {
70
+ 'left': 'inline-start',
71
+ 'right': 'inline-end',
72
+ },
73
+ };
74
+
75
+ function escapeRegex(string) {
76
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
77
+ }
78
+
79
+ function fixLogicalProperties(content) {
80
+ // Process line by line to avoid matching in comments
81
+ const lines = content.split('\n');
82
+ const fixedLines = lines.map(line => {
83
+ // Skip comment lines (LESS/CSS comments)
84
+ const trimmed = line.trim();
85
+ if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*')) {
86
+ return line;
87
+ }
88
+
89
+ let fixedLine = line;
90
+
91
+ // Fix property names - match property at start of declaration
92
+ for (const [physical, logical] of Object.entries(PROPERTY_MAP)) {
93
+ // Match: whitespace + property + colon (with optional whitespace)
94
+ // This ensures we only match actual CSS properties, not parts of other words
95
+ const propertyRegex = new RegExp(
96
+ `(^\\s*|[{;]\\s*)${escapeRegex(physical)}(\\s*:)`,
97
+ 'g'
98
+ );
99
+ fixedLine = fixedLine.replace(propertyRegex, `$1${logical}$2`);
100
+ }
101
+
102
+ // Fix values for specific properties
103
+ for (const [property, values] of Object.entries(VALUE_MAP)) {
104
+ for (const [physical, logical] of Object.entries(values)) {
105
+ // Match: property: value (with proper boundaries)
106
+ const valueRegex = new RegExp(
107
+ `(${escapeRegex(property)}\\s*:\\s*)${physical}(\\s*[;!}]|\\s*$)`,
108
+ 'gi'
109
+ );
110
+ fixedLine = fixedLine.replace(valueRegex, `$1${logical}$2`);
111
+ }
112
+ }
113
+
114
+ return fixedLine;
115
+ });
116
+
117
+ return fixedLines.join('\n');
118
+ }
119
+
120
+ function processStyleBlocks(content) {
121
+ // Match all <style> blocks (with any attributes like lang, scoped, etc.)
122
+ const styleRegex = /(<style[^>]*>)([\s\S]*?)(<\/style>)/gi;
123
+ let hasChanges = false;
124
+
125
+ const fixed = content.replace(styleRegex, (_match, openTag, styleContent, closeTag) => {
126
+ const fixedStyle = fixLogicalProperties(styleContent);
127
+ if (fixedStyle !== styleContent) {
128
+ hasChanges = true;
129
+ }
130
+ return openTag + fixedStyle + closeTag;
131
+ });
132
+
133
+ return { fixed, hasChanges };
134
+ }
135
+
136
+ function processMarkdownFile(filePath) {
137
+ try {
138
+ const content = fs.readFileSync(filePath, 'utf8');
139
+
140
+ // Split by fenced code blocks to avoid processing code examples
141
+ // Match ``` with optional language identifier and content until closing ```
142
+ const fencedCodeRegex = /(```[\s\S]*?```)/g;
143
+ const parts = content.split(fencedCodeRegex);
144
+ let hasChanges = false;
145
+
146
+ // Process only non-code-block parts
147
+ const fixedParts = parts.map((part, index) => {
148
+ // Odd indices are the fenced code blocks (captured groups)
149
+ if (index % 2 === 1) {
150
+ return part; // Keep code blocks unchanged
151
+ }
152
+ // Process style blocks in non-code parts
153
+ const result = processStyleBlocks(part);
154
+ if (result.hasChanges) {
155
+ hasChanges = true;
156
+ }
157
+ return result.fixed;
158
+ });
159
+
160
+ if (hasChanges) {
161
+ fs.writeFileSync(filePath, fixedParts.join(''), 'utf8');
162
+ console.log(`Fixed: ${filePath}`);
163
+ return true;
164
+ }
165
+ return false;
166
+ } catch (error) {
167
+ console.error(`Error processing ${filePath}:`, error.message);
168
+ return false;
169
+ }
170
+ }
171
+
172
+ function processFileWithStyleBlocks(filePath) {
173
+ try {
174
+ const content = fs.readFileSync(filePath, 'utf8');
175
+ const result = processStyleBlocks(content);
176
+
177
+ if (result.hasChanges) {
178
+ fs.writeFileSync(filePath, result.fixed, 'utf8');
179
+ console.log(`Fixed: ${filePath}`);
180
+ return true;
181
+ }
182
+ return false;
183
+ } catch (error) {
184
+ console.error(`Error processing ${filePath}:`, error.message);
185
+ return false;
186
+ }
187
+ }
188
+
189
+ function processFile(filePath) {
190
+ try {
191
+ // Markdown: skip fenced code blocks, only process <style> tags
192
+ if (filePath.endsWith('.md')) {
193
+ return processMarkdownFile(filePath);
194
+ }
195
+
196
+ // Vue/HTML: only process <style> blocks
197
+ if (filePath.endsWith('.vue') || filePath.endsWith('.html')) {
198
+ return processFileWithStyleBlocks(filePath);
199
+ }
200
+
201
+ // CSS/LESS: process entire file
202
+ const content = fs.readFileSync(filePath, 'utf8');
203
+ const fixed = fixLogicalProperties(content);
204
+
205
+ if (content !== fixed) {
206
+ fs.writeFileSync(filePath, fixed, 'utf8');
207
+ console.log(`Fixed: ${filePath}`);
208
+ return true;
209
+ }
210
+ return false;
211
+ } catch (error) {
212
+ console.error(`Error processing ${filePath}:`, error.message);
213
+ return false;
214
+ }
215
+ }
216
+
217
+ // Main: process files passed as arguments
218
+ const files = process.argv.slice(2);
219
+
220
+ if (files.length === 0) {
221
+ console.log('Usage: fix-logical-properties <file1> [file2] ...');
222
+ } else {
223
+ let fixedCount = 0;
224
+ for (const file of files) {
225
+ if (processFile(file)) {
226
+ fixedCount++;
227
+ }
228
+ }
229
+
230
+ if (fixedCount > 0) {
231
+ console.log(`\nFixed ${fixedCount} file(s)`);
232
+ }
233
+ }
package/lib/index.js CHANGED
@@ -5,6 +5,7 @@ const noDeprecatedSpaceTokens = require('./rules/no-deprecated-space-tokens');
5
5
  const noMixins = require('./rules/no-mixins');
6
6
  const recommendFontStyleTokens = require('./rules/recommend-font-style-tokens');
7
7
  const useDialtoneTokens = require('./rules/use-dialtone-tokens');
8
+ const useLogical = require('stylelint-use-logical');
8
9
 
9
10
  module.exports = [
10
11
  noBaseColorTokens,
@@ -12,4 +13,5 @@ module.exports = [
12
13
  noMixins,
13
14
  recommendFontStyleTokens,
14
15
  useDialtoneTokens,
16
+ useLogical,
15
17
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dialpad/stylelint-plugin-dialtone",
3
- "version": "1.3.1-next.1",
3
+ "version": "1.4.0-next.1",
4
4
  "description": "dialtone stylelint plugin",
5
5
  "keywords": [
6
6
  "Dialpad",
@@ -39,10 +39,17 @@
39
39
  "directory": "packages/stylelint-plugin-dialtone"
40
40
  },
41
41
  "main": "./lib/index.js",
42
+ "bin": {
43
+ "fix-logical-properties": "./bin/fix-logical-properties.js"
44
+ },
42
45
  "files": [
43
- "lib"
46
+ "lib",
47
+ "bin"
44
48
  ],
45
49
  "exports": "./lib/index.js",
50
+ "dependencies": {
51
+ "stylelint-use-logical": "^2.1.2"
52
+ },
46
53
  "devDependencies": {
47
54
  "eslint-plugin-n": "^17.0.0",
48
55
  "stylelint-test-rule-node": "^0.2.1",