scss-variable-extractor 1.6.0 → 1.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scss-variable-extractor",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
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
- // Match the value with word boundaries or specific contexts
113
- return new RegExp(escaped, 'g');
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,20 @@ 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 if it would create invalid variable concatenation
157
+ if (charBefore === '$' || charAfter === '$') {
158
+ return true;
159
+ }
160
+
130
161
  // Check for @use, @forward, @import
131
162
  if (/@(?:use|forward|import)/.test(line)) {
132
163
  return true;
@@ -124,4 +124,47 @@ 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
+ });
127
170
  });