scss-variable-extractor 1.6.0 → 1.6.3
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/package.json +5 -2
- package/src/refactorer.js +44 -4
- package/test/refactorer.test.js +84 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scss-variable-extractor",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"description": "Analyzes Angular SCSS files and extracts repeated hardcoded values into reusable variables",
|
|
5
5
|
"bin": {
|
|
6
6
|
"scss-extract": "./bin/cli.js"
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
"analyze-patterns": "node bin/cli.js analyze-patterns .",
|
|
15
15
|
"modernize": "node bin/cli.js modernize .",
|
|
16
16
|
"detect-bootstrap": "node bin/cli.js detect-bootstrap .",
|
|
17
|
-
"migrate-bootstrap": "node bin/cli.js migrate-bootstrap ."
|
|
17
|
+
"migrate-bootstrap": "node bin/cli.js migrate-bootstrap .",
|
|
18
|
+
"release": "npm test && npm version patch && npm publish && git push --follow-tags",
|
|
19
|
+
"release:minor": "npm test && npm version minor && npm publish && git push --follow-tags",
|
|
20
|
+
"release:major": "npm test && npm version major && npm publish && git push --follow-tags"
|
|
18
21
|
},
|
|
19
22
|
"keywords": [
|
|
20
23
|
"angular",
|
package/src/refactorer.js
CHANGED
|
@@ -57,7 +57,7 @@ function refactorFile(content, analysis, variablesFilePath, currentFilePath, con
|
|
|
57
57
|
|
|
58
58
|
// Check if replacements are in safe zones
|
|
59
59
|
matches.forEach(m => {
|
|
60
|
-
if (!isInSafeZone(content, m.index)) {
|
|
60
|
+
if (!isInSafeZone(content, m.index, m.matched)) {
|
|
61
61
|
// Replace the value
|
|
62
62
|
const before = newContent;
|
|
63
63
|
newContent = replaceAt(newContent, m.matched, variableName, m.index);
|
|
@@ -109,14 +109,31 @@ function buildValueMap(analysis) {
|
|
|
109
109
|
function createReplacementRegex(value) {
|
|
110
110
|
// Escape special regex characters
|
|
111
111
|
const escaped = value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
|
|
113
|
+
// Add appropriate boundaries based on value type to prevent partial matches
|
|
114
|
+
|
|
115
|
+
if (/^#[0-9a-fA-F]+$/.test(value)) {
|
|
116
|
+
// Hex colors - no word boundary before #, but check after
|
|
117
|
+
return new RegExp(`${escaped}\\b`, 'g');
|
|
118
|
+
} else if (/\d+(?:px|%|em|rem|vh|vw|pt|ch|ex)$/.test(value)) {
|
|
119
|
+
// Values with CSS units - ensure not part of larger number
|
|
120
|
+
return new RegExp(`(?<!\\d)${escaped}\\b`, 'g');
|
|
121
|
+
} else if (/^-?\d+(?:\.\d+)?$/.test(value)) {
|
|
122
|
+
// Pure numbers (including decimals) - ensure not part of larger number
|
|
123
|
+
return new RegExp(`(?<!\\d)${escaped}(?!\\d)`, 'g');
|
|
124
|
+
} else if (/^rgba?\(/.test(value)) {
|
|
125
|
+
// RGB/RGBA colors - match complete function
|
|
126
|
+
return new RegExp(escaped, 'g');
|
|
127
|
+
} else {
|
|
128
|
+
// Other values (keywords, etc.) - use word boundaries
|
|
129
|
+
return new RegExp(`\\b${escaped}\\b`, 'g');
|
|
130
|
+
}
|
|
114
131
|
}
|
|
115
132
|
|
|
116
133
|
/**
|
|
117
134
|
* Checks if a position in content is in a safe zone (should not be replaced)
|
|
118
135
|
*/
|
|
119
|
-
function isInSafeZone(content, position) {
|
|
136
|
+
function isInSafeZone(content, position, matchedValue) {
|
|
120
137
|
// Check for variable declarations
|
|
121
138
|
const lineStart = content.lastIndexOf('\n', position);
|
|
122
139
|
const lineEnd = content.indexOf('\n', position);
|
|
@@ -127,6 +144,29 @@ function isInSafeZone(content, position) {
|
|
|
127
144
|
return true;
|
|
128
145
|
}
|
|
129
146
|
|
|
147
|
+
// Check if we're in the middle of a larger number or value
|
|
148
|
+
const charBefore = content.charAt(position - 1);
|
|
149
|
+
const charAfter = content.charAt(position + matchedValue.length);
|
|
150
|
+
|
|
151
|
+
// Prevent replacement if surrounded by digits (partial number match)
|
|
152
|
+
if (/\d/.test(charBefore) || (/^\d+$/.test(matchedValue) && /\d/.test(charAfter))) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Prevent replacement within hex colors
|
|
157
|
+
if (/[0-9a-fA-F]/.test(charBefore) || /[0-9a-fA-F]/.test(charAfter)) {
|
|
158
|
+
// Look back further to check if we're in a hex color
|
|
159
|
+
const lookBehind = content.substring(Math.max(0, position - 10), position);
|
|
160
|
+
if (/#[0-9a-fA-F]*$/.test(lookBehind)) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Prevent replacement if it would create invalid variable concatenation
|
|
166
|
+
if (charBefore === '$' || charAfter === '$') {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
130
170
|
// Check for @use, @forward, @import
|
|
131
171
|
if (/@(?:use|forward|import)/.test(line)) {
|
|
132
172
|
return true;
|
package/test/refactorer.test.js
CHANGED
|
@@ -124,4 +124,88 @@ describe('Refactorer', () => {
|
|
|
124
124
|
// Should not replace in variable declaration
|
|
125
125
|
expect(result.content).toContain('$my-color: #1976d2');
|
|
126
126
|
});
|
|
127
|
+
|
|
128
|
+
test('should not do partial replacements within larger values', () => {
|
|
129
|
+
const scss = `
|
|
130
|
+
@media (min-width: 768px) {
|
|
131
|
+
.container {
|
|
132
|
+
width: 233px;
|
|
133
|
+
padding: 8px;
|
|
134
|
+
z-index: 100;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
const analysis = {
|
|
140
|
+
colors: [],
|
|
141
|
+
spacing: [
|
|
142
|
+
{ value: '8px', suggestedName: '$spacing-sm' }
|
|
143
|
+
],
|
|
144
|
+
fontSizes: [],
|
|
145
|
+
fontWeights: [],
|
|
146
|
+
fontFamilies: [],
|
|
147
|
+
borderRadius: [],
|
|
148
|
+
shadows: [],
|
|
149
|
+
zIndex: [
|
|
150
|
+
{ value: '100', suggestedName: '$z-index-modal' }
|
|
151
|
+
],
|
|
152
|
+
sizing: [],
|
|
153
|
+
lineHeight: [],
|
|
154
|
+
opacity: [],
|
|
155
|
+
transitions: []
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const result = refactorFile(scss, analysis, '/libs/styles/_variables.scss', '/app/test.scss', {});
|
|
159
|
+
|
|
160
|
+
// Should replace complete values
|
|
161
|
+
expect(result.content).toContain('padding: $spacing-sm');
|
|
162
|
+
expect(result.content).toContain('z-index: $z-index-modal');
|
|
163
|
+
|
|
164
|
+
// Should NOT do partial replacements
|
|
165
|
+
expect(result.content).toContain('768px'); // Not 76$spacing-smpx
|
|
166
|
+
expect(result.content).toContain('233px'); // Not 2$spacing-sm$spacing-smpx
|
|
167
|
+
expect(result.content).not.toContain('76$spacing-sm');
|
|
168
|
+
expect(result.content).not.toContain('$z-index23');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('should not replace characters within hex colors', () => {
|
|
172
|
+
const scss = `
|
|
173
|
+
.component {
|
|
174
|
+
background: #a2dab1;
|
|
175
|
+
border-color: #ff2244;
|
|
176
|
+
color: #333;
|
|
177
|
+
}
|
|
178
|
+
`;
|
|
179
|
+
|
|
180
|
+
const analysis = {
|
|
181
|
+
colors: [
|
|
182
|
+
{ value: '#a2dab1', suggestedName: '$color-success' },
|
|
183
|
+
{ value: '#333', suggestedName: '$color-text' }
|
|
184
|
+
],
|
|
185
|
+
spacing: [],
|
|
186
|
+
fontSizes: [],
|
|
187
|
+
fontWeights: [],
|
|
188
|
+
fontFamilies: [],
|
|
189
|
+
borderRadius: [],
|
|
190
|
+
shadows: [],
|
|
191
|
+
zIndex: [
|
|
192
|
+
{ value: '2', suggestedName: '$z-index-2' }
|
|
193
|
+
],
|
|
194
|
+
sizing: [],
|
|
195
|
+
lineHeight: [],
|
|
196
|
+
opacity: [],
|
|
197
|
+
transitions: []
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const result = refactorFile(scss, analysis, '/libs/styles/_variables.scss', '/app/test.scss', {});
|
|
201
|
+
|
|
202
|
+
// Should replace complete hex colors
|
|
203
|
+
expect(result.content).toContain('background: $color-success');
|
|
204
|
+
expect(result.content).toContain('color: $color-text');
|
|
205
|
+
|
|
206
|
+
// Should NOT replace digits within hex colors
|
|
207
|
+
expect(result.content).toContain('#ff2244'); // Not #ff$z-index-2$z-index-244
|
|
208
|
+
expect(result.content).not.toContain('#a$z-index-2dab1');
|
|
209
|
+
expect(result.content).not.toContain('#ff$z-index-2');
|
|
210
|
+
});
|
|
127
211
|
});
|