scss-variable-extractor 1.0.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/.scssextractrc.example.json +35 -0
- package/LICENSE +21 -0
- package/README.md +423 -0
- package/bin/cli.js +226 -0
- package/jest.config.js +12 -0
- package/package.json +38 -0
- package/src/analyzer.js +285 -0
- package/src/config.js +82 -0
- package/src/generator.js +219 -0
- package/src/index.js +16 -0
- package/src/parser.js +421 -0
- package/src/refactorer.js +209 -0
- package/src/scanner.js +29 -0
- package/templates/_variables.scss.template +56 -0
- package/test/analyzer.test.js +107 -0
- package/test/fixtures/apps/subapp/src/app/component-a/component-a.component.scss +47 -0
- package/test/fixtures/apps/subapp/src/app/component-b/component-b.component.scss +52 -0
- package/test/fixtures/libs/styles/_existing-variables.scss +16 -0
- package/test/generator.test.js +149 -0
- package/test/parser.test.js +131 -0
- package/test/refactorer.test.js +127 -0
- package/test/scanner.test.js +25 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const { analyzeValues, generateVariableName } = require('../src/analyzer');
|
|
2
|
+
const { DEFAULT_CONFIG } = require('../src/config');
|
|
3
|
+
|
|
4
|
+
describe('Analyzer', () => {
|
|
5
|
+
test('should count repeated values', () => {
|
|
6
|
+
const extracted = {
|
|
7
|
+
colors: [
|
|
8
|
+
{ value: '#1976d2', file: 'a.scss', line: 1 },
|
|
9
|
+
{ value: '#1976d2', file: 'b.scss', line: 1 },
|
|
10
|
+
{ value: '#fff', file: 'a.scss', line: 2 }
|
|
11
|
+
],
|
|
12
|
+
spacing: [],
|
|
13
|
+
fontSizes: [],
|
|
14
|
+
fontWeights: [],
|
|
15
|
+
fontFamilies: [],
|
|
16
|
+
borderRadius: [],
|
|
17
|
+
shadows: [],
|
|
18
|
+
zIndex: [],
|
|
19
|
+
sizing: [],
|
|
20
|
+
lineHeight: [],
|
|
21
|
+
opacity: [],
|
|
22
|
+
transitions: []
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const analysis = analyzeValues(extracted, DEFAULT_CONFIG);
|
|
26
|
+
|
|
27
|
+
expect(analysis.colors.length).toBeGreaterThan(0);
|
|
28
|
+
const primaryColor = analysis.colors.find(c => c.value === '#1976d2');
|
|
29
|
+
expect(primaryColor.count).toBe(2);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('should filter by threshold', () => {
|
|
33
|
+
const extracted = {
|
|
34
|
+
colors: [
|
|
35
|
+
{ value: '#1976d2', file: 'a.scss', line: 1 },
|
|
36
|
+
{ value: '#fff', file: 'a.scss', line: 2 }
|
|
37
|
+
],
|
|
38
|
+
spacing: [],
|
|
39
|
+
fontSizes: [],
|
|
40
|
+
fontWeights: [],
|
|
41
|
+
fontFamilies: [],
|
|
42
|
+
borderRadius: [],
|
|
43
|
+
shadows: [],
|
|
44
|
+
zIndex: [],
|
|
45
|
+
sizing: [],
|
|
46
|
+
lineHeight: [],
|
|
47
|
+
opacity: [],
|
|
48
|
+
transitions: []
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const config = { ...DEFAULT_CONFIG, threshold: 2 };
|
|
52
|
+
const analysis = analyzeValues(extracted, config);
|
|
53
|
+
|
|
54
|
+
// No colors should pass threshold of 2
|
|
55
|
+
expect(analysis.colors.length).toBe(0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('should generate spacing variable names with t-shirt sizing', () => {
|
|
59
|
+
const name = generateVariableName('spacing', '16px', [], DEFAULT_CONFIG);
|
|
60
|
+
|
|
61
|
+
expect(name).toBe('$spacing-md');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('should generate color variable names', () => {
|
|
65
|
+
const name = generateVariableName('colors', '#1976d2', []);
|
|
66
|
+
|
|
67
|
+
expect(name).toContain('$color-');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('should generate font-size variable names', () => {
|
|
71
|
+
const name = generateVariableName('fontSizes', '14px', []);
|
|
72
|
+
|
|
73
|
+
expect(name).toBe('$font-size-sm');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('should generate font-weight variable names', () => {
|
|
77
|
+
const name = generateVariableName('fontWeights', '500', []);
|
|
78
|
+
|
|
79
|
+
expect(name).toBe('$font-weight-medium');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('should count unique files', () => {
|
|
83
|
+
const extracted = {
|
|
84
|
+
colors: [
|
|
85
|
+
{ value: '#1976d2', file: 'a.scss', line: 1 },
|
|
86
|
+
{ value: '#1976d2', file: 'a.scss', line: 5 },
|
|
87
|
+
{ value: '#1976d2', file: 'b.scss', line: 1 }
|
|
88
|
+
],
|
|
89
|
+
spacing: [],
|
|
90
|
+
fontSizes: [],
|
|
91
|
+
fontWeights: [],
|
|
92
|
+
fontFamilies: [],
|
|
93
|
+
borderRadius: [],
|
|
94
|
+
shadows: [],
|
|
95
|
+
zIndex: [],
|
|
96
|
+
sizing: [],
|
|
97
|
+
lineHeight: [],
|
|
98
|
+
opacity: [],
|
|
99
|
+
transitions: []
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const analysis = analyzeValues(extracted, DEFAULT_CONFIG);
|
|
103
|
+
|
|
104
|
+
const primaryColor = analysis.colors.find(c => c.value === '#1976d2');
|
|
105
|
+
expect(primaryColor.fileCount).toBe(2);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
.component-a {
|
|
2
|
+
background-color: #1976d2;
|
|
3
|
+
color: rgba(0, 0, 0, 0.87);
|
|
4
|
+
padding: 16px;
|
|
5
|
+
margin: 8px;
|
|
6
|
+
font-size: 14px;
|
|
7
|
+
font-weight: 500;
|
|
8
|
+
border-radius: 4px;
|
|
9
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
10
|
+
|
|
11
|
+
.header {
|
|
12
|
+
background-color: #1976d2;
|
|
13
|
+
padding: 24px;
|
|
14
|
+
margin-bottom: 16px;
|
|
15
|
+
font-size: 20px;
|
|
16
|
+
font-weight: 700;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.content {
|
|
20
|
+
padding: 16px;
|
|
21
|
+
font-size: 14px;
|
|
22
|
+
line-height: 1.5;
|
|
23
|
+
color: rgba(0, 0, 0, 0.87);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.button {
|
|
27
|
+
padding: 8px 16px;
|
|
28
|
+
background-color: #1976d2;
|
|
29
|
+
color: white;
|
|
30
|
+
border-radius: 4px;
|
|
31
|
+
font-weight: 500;
|
|
32
|
+
transition: all 0.3s ease;
|
|
33
|
+
|
|
34
|
+
&:hover {
|
|
35
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
36
|
+
opacity: 0.9;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.footer {
|
|
41
|
+
margin-top: 24px;
|
|
42
|
+
padding: 8px;
|
|
43
|
+
font-size: 12px;
|
|
44
|
+
color: rgba(0, 0, 0, 0.54);
|
|
45
|
+
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
.component-b {
|
|
2
|
+
background-color: white;
|
|
3
|
+
padding: 16px;
|
|
4
|
+
margin: 8px;
|
|
5
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
6
|
+
border-radius: 4px;
|
|
7
|
+
|
|
8
|
+
.title {
|
|
9
|
+
color: #1976d2;
|
|
10
|
+
font-size: 20px;
|
|
11
|
+
font-weight: 700;
|
|
12
|
+
margin-bottom: 16px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.description {
|
|
16
|
+
color: rgba(0, 0, 0, 0.87);
|
|
17
|
+
font-size: 14px;
|
|
18
|
+
line-height: 1.5;
|
|
19
|
+
padding: 8px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.actions {
|
|
23
|
+
margin-top: 24px;
|
|
24
|
+
padding-top: 16px;
|
|
25
|
+
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
|
26
|
+
|
|
27
|
+
.action-button {
|
|
28
|
+
padding: 8px 16px;
|
|
29
|
+
margin-right: 8px;
|
|
30
|
+
background-color: #1976d2;
|
|
31
|
+
color: white;
|
|
32
|
+
border-radius: 4px;
|
|
33
|
+
font-weight: 500;
|
|
34
|
+
transition: all 0.3s ease;
|
|
35
|
+
|
|
36
|
+
&:hover {
|
|
37
|
+
opacity: 0.9;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&.secondary {
|
|
41
|
+
background-color: rgba(0, 0, 0, 0.1);
|
|
42
|
+
color: rgba(0, 0, 0, 0.87);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.metadata {
|
|
48
|
+
font-size: 12px;
|
|
49
|
+
color: rgba(0, 0, 0, 0.54);
|
|
50
|
+
padding: 8px;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Existing Variables
|
|
2
|
+
// These should not be extracted again
|
|
3
|
+
|
|
4
|
+
$existing-primary-color: #2196f3;
|
|
5
|
+
$existing-spacing: 12px;
|
|
6
|
+
$existing-font-size: 16px;
|
|
7
|
+
|
|
8
|
+
// Color palette
|
|
9
|
+
$palette-red: #f44336;
|
|
10
|
+
$palette-green: #4caf50;
|
|
11
|
+
$palette-blue: #2196f3;
|
|
12
|
+
|
|
13
|
+
// Spacing scale
|
|
14
|
+
$space-small: 8px;
|
|
15
|
+
$space-medium: 16px;
|
|
16
|
+
$space-large: 24px;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const { generateVariablesFile, generateReport } = require('../src/generator');
|
|
2
|
+
const { DEFAULT_CONFIG } = require('../src/config');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
describe('Generator', () => {
|
|
8
|
+
let tempDir;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'scss-test-'));
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
if (fs.existsSync(tempDir)) {
|
|
16
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('should generate variables file', () => {
|
|
21
|
+
const analysis = {
|
|
22
|
+
colors: [
|
|
23
|
+
{ value: '#1976d2', count: 5, suggestedName: '$color-brand-primary' }
|
|
24
|
+
],
|
|
25
|
+
spacing: [
|
|
26
|
+
{ value: '16px', count: 10, suggestedName: '$spacing-md' }
|
|
27
|
+
],
|
|
28
|
+
fontSizes: [],
|
|
29
|
+
fontWeights: [],
|
|
30
|
+
fontFamilies: [],
|
|
31
|
+
borderRadius: [],
|
|
32
|
+
shadows: [],
|
|
33
|
+
zIndex: [],
|
|
34
|
+
sizing: [],
|
|
35
|
+
lineHeight: [],
|
|
36
|
+
opacity: [],
|
|
37
|
+
transitions: []
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const outputPath = path.join(tempDir, '_variables.scss');
|
|
41
|
+
generateVariablesFile(analysis, outputPath, DEFAULT_CONFIG);
|
|
42
|
+
|
|
43
|
+
expect(fs.existsSync(outputPath)).toBe(true);
|
|
44
|
+
|
|
45
|
+
const content = fs.readFileSync(outputPath, 'utf8');
|
|
46
|
+
expect(content).toContain('$color-brand-primary');
|
|
47
|
+
expect(content).toContain('#1976d2');
|
|
48
|
+
expect(content).toContain('$spacing-md');
|
|
49
|
+
expect(content).toContain('16px');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('should include header with version and timestamp', () => {
|
|
53
|
+
const analysis = {
|
|
54
|
+
colors: [],
|
|
55
|
+
spacing: [],
|
|
56
|
+
fontSizes: [],
|
|
57
|
+
fontWeights: [],
|
|
58
|
+
fontFamilies: [],
|
|
59
|
+
borderRadius: [],
|
|
60
|
+
shadows: [],
|
|
61
|
+
zIndex: [],
|
|
62
|
+
sizing: [],
|
|
63
|
+
lineHeight: [],
|
|
64
|
+
opacity: [],
|
|
65
|
+
transitions: []
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const outputPath = path.join(tempDir, '_variables.scss');
|
|
69
|
+
const content = generateVariablesFile(analysis, outputPath, DEFAULT_CONFIG);
|
|
70
|
+
|
|
71
|
+
expect(content).toContain('Auto-generated SCSS Variables');
|
|
72
|
+
expect(content).toContain('scss-variable-extractor');
|
|
73
|
+
expect(content).toContain('DO NOT EDIT');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('should generate table report', () => {
|
|
77
|
+
const analysis = {
|
|
78
|
+
colors: [
|
|
79
|
+
{ value: '#1976d2', count: 5, fileCount: 3, suggestedName: '$color-brand-primary' }
|
|
80
|
+
],
|
|
81
|
+
spacing: [],
|
|
82
|
+
fontSizes: [],
|
|
83
|
+
fontWeights: [],
|
|
84
|
+
fontFamilies: [],
|
|
85
|
+
borderRadius: [],
|
|
86
|
+
shadows: [],
|
|
87
|
+
zIndex: [],
|
|
88
|
+
sizing: [],
|
|
89
|
+
lineHeight: [],
|
|
90
|
+
opacity: [],
|
|
91
|
+
transitions: []
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const report = generateReport(analysis, 'table', DEFAULT_CONFIG);
|
|
95
|
+
|
|
96
|
+
expect(report).toContain('#1976d2');
|
|
97
|
+
expect(report).toContain('$color-brand-primary');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('should generate JSON report', () => {
|
|
101
|
+
const analysis = {
|
|
102
|
+
colors: [
|
|
103
|
+
{ value: '#1976d2', count: 5, suggestedName: '$color-brand-primary' }
|
|
104
|
+
],
|
|
105
|
+
spacing: [],
|
|
106
|
+
fontSizes: [],
|
|
107
|
+
fontWeights: [],
|
|
108
|
+
fontFamilies: [],
|
|
109
|
+
borderRadius: [],
|
|
110
|
+
shadows: [],
|
|
111
|
+
zIndex: [],
|
|
112
|
+
sizing: [],
|
|
113
|
+
lineHeight: [],
|
|
114
|
+
opacity: [],
|
|
115
|
+
transitions: []
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const report = generateReport(analysis, 'json', DEFAULT_CONFIG);
|
|
119
|
+
const parsed = JSON.parse(report);
|
|
120
|
+
|
|
121
|
+
expect(parsed.colors).toHaveLength(1);
|
|
122
|
+
expect(parsed.colors[0].value).toBe('#1976d2');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('should generate Markdown report', () => {
|
|
126
|
+
const analysis = {
|
|
127
|
+
colors: [
|
|
128
|
+
{ value: '#1976d2', count: 5, fileCount: 3, suggestedName: '$color-brand-primary' }
|
|
129
|
+
],
|
|
130
|
+
spacing: [],
|
|
131
|
+
fontSizes: [],
|
|
132
|
+
fontWeights: [],
|
|
133
|
+
fontFamilies: [],
|
|
134
|
+
borderRadius: [],
|
|
135
|
+
shadows: [],
|
|
136
|
+
zIndex: [],
|
|
137
|
+
sizing: [],
|
|
138
|
+
lineHeight: [],
|
|
139
|
+
opacity: [],
|
|
140
|
+
transitions: []
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const report = generateReport(analysis, 'markdown', DEFAULT_CONFIG);
|
|
144
|
+
|
|
145
|
+
expect(report).toContain('# SCSS Variable Extraction Analysis');
|
|
146
|
+
expect(report).toContain('## Colors');
|
|
147
|
+
expect(report).toContain('#1976d2');
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
const { parseScss } = require('../src/parser');
|
|
2
|
+
|
|
3
|
+
describe('Parser', () => {
|
|
4
|
+
test('should extract hex colors', () => {
|
|
5
|
+
const scss = `
|
|
6
|
+
.test {
|
|
7
|
+
color: #1976d2;
|
|
8
|
+
background: #fff;
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const result = parseScss(scss, 'test.scss');
|
|
13
|
+
|
|
14
|
+
expect(result.colors.length).toBeGreaterThan(0);
|
|
15
|
+
expect(result.colors.some(c => c.value === '#1976d2')).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('should extract rgba colors', () => {
|
|
19
|
+
const scss = `
|
|
20
|
+
.test {
|
|
21
|
+
color: rgba(0, 0, 0, 0.87);
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
const result = parseScss(scss, 'test.scss');
|
|
26
|
+
|
|
27
|
+
expect(result.colors.some(c => c.value.includes('rgba'))).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('should extract spacing values', () => {
|
|
31
|
+
const scss = `
|
|
32
|
+
.test {
|
|
33
|
+
padding: 16px;
|
|
34
|
+
margin: 8px;
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const result = parseScss(scss, 'test.scss');
|
|
39
|
+
|
|
40
|
+
expect(result.spacing.length).toBeGreaterThan(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('should extract font sizes', () => {
|
|
44
|
+
const scss = `
|
|
45
|
+
.test {
|
|
46
|
+
font-size: 14px;
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
const result = parseScss(scss, 'test.scss');
|
|
51
|
+
|
|
52
|
+
expect(result.fontSizes.length).toBeGreaterThan(0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('should extract font weights', () => {
|
|
56
|
+
const scss = `
|
|
57
|
+
.test {
|
|
58
|
+
font-weight: 500;
|
|
59
|
+
font-weight: bold;
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
const result = parseScss(scss, 'test.scss');
|
|
64
|
+
|
|
65
|
+
expect(result.fontWeights.length).toBeGreaterThan(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('should not extract from variable declarations', () => {
|
|
69
|
+
const scss = `
|
|
70
|
+
$my-color: #1976d2;
|
|
71
|
+
.test {
|
|
72
|
+
color: $my-color;
|
|
73
|
+
}
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
const result = parseScss(scss, 'test.scss');
|
|
77
|
+
|
|
78
|
+
// Should not extract the color from variable declaration
|
|
79
|
+
expect(result.colors.length).toBe(0);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('should not extract from url() functions', () => {
|
|
83
|
+
const scss = `
|
|
84
|
+
.test {
|
|
85
|
+
background: url('image.png');
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
const result = parseScss(scss, 'test.scss');
|
|
90
|
+
|
|
91
|
+
// Should not extract anything from url
|
|
92
|
+
expect(result.colors.length).toBe(0);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('should extract border radius', () => {
|
|
96
|
+
const scss = `
|
|
97
|
+
.test {
|
|
98
|
+
border-radius: 4px;
|
|
99
|
+
border-radius: 50%;
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
const result = parseScss(scss, 'test.scss');
|
|
104
|
+
|
|
105
|
+
expect(result.borderRadius.length).toBeGreaterThan(0);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('should extract z-index values', () => {
|
|
109
|
+
const scss = `
|
|
110
|
+
.test {
|
|
111
|
+
z-index: 100;
|
|
112
|
+
}
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
const result = parseScss(scss, 'test.scss');
|
|
116
|
+
|
|
117
|
+
expect(result.zIndex.length).toBeGreaterThan(0);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('should extract opacity values', () => {
|
|
121
|
+
const scss = `
|
|
122
|
+
.test {
|
|
123
|
+
opacity: 0.9;
|
|
124
|
+
}
|
|
125
|
+
`;
|
|
126
|
+
|
|
127
|
+
const result = parseScss(scss, 'test.scss');
|
|
128
|
+
|
|
129
|
+
expect(result.opacity.length).toBeGreaterThan(0);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const { refactorFile } = require('../src/refactorer');
|
|
2
|
+
|
|
3
|
+
describe('Refactorer', () => {
|
|
4
|
+
test('should replace hardcoded values with variables', () => {
|
|
5
|
+
const scss = `
|
|
6
|
+
.test {
|
|
7
|
+
color: #1976d2;
|
|
8
|
+
padding: 16px;
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const analysis = {
|
|
13
|
+
colors: [
|
|
14
|
+
{ value: '#1976d2', suggestedName: '$color-brand-primary' }
|
|
15
|
+
],
|
|
16
|
+
spacing: [
|
|
17
|
+
{ value: '16px', suggestedName: '$spacing-md' }
|
|
18
|
+
],
|
|
19
|
+
fontSizes: [],
|
|
20
|
+
fontWeights: [],
|
|
21
|
+
fontFamilies: [],
|
|
22
|
+
borderRadius: [],
|
|
23
|
+
shadows: [],
|
|
24
|
+
zIndex: [],
|
|
25
|
+
sizing: [],
|
|
26
|
+
lineHeight: [],
|
|
27
|
+
opacity: [],
|
|
28
|
+
transitions: []
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const result = refactorFile(scss, analysis, '/libs/styles/_variables.scss', '/app/test.scss', {});
|
|
32
|
+
|
|
33
|
+
expect(result.modified).toBe(true);
|
|
34
|
+
expect(result.content).toContain('$color-brand-primary');
|
|
35
|
+
expect(result.content).toContain('$spacing-md');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('should add @use import when modified', () => {
|
|
39
|
+
const scss = `
|
|
40
|
+
.test {
|
|
41
|
+
color: #1976d2;
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
const analysis = {
|
|
46
|
+
colors: [
|
|
47
|
+
{ value: '#1976d2', suggestedName: '$color-brand-primary' }
|
|
48
|
+
],
|
|
49
|
+
spacing: [],
|
|
50
|
+
fontSizes: [],
|
|
51
|
+
fontWeights: [],
|
|
52
|
+
fontFamilies: [],
|
|
53
|
+
borderRadius: [],
|
|
54
|
+
shadows: [],
|
|
55
|
+
zIndex: [],
|
|
56
|
+
sizing: [],
|
|
57
|
+
lineHeight: [],
|
|
58
|
+
opacity: [],
|
|
59
|
+
transitions: []
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const result = refactorFile(scss, analysis, '/libs/styles/_variables.scss', '/app/test.scss', {});
|
|
63
|
+
|
|
64
|
+
expect(result.content).toContain('@use');
|
|
65
|
+
expect(result.content).toContain('variables');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('should not modify if no values match', () => {
|
|
69
|
+
const scss = `
|
|
70
|
+
.test {
|
|
71
|
+
color: #000;
|
|
72
|
+
}
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
const analysis = {
|
|
76
|
+
colors: [
|
|
77
|
+
{ value: '#1976d2', suggestedName: '$color-brand-primary' }
|
|
78
|
+
],
|
|
79
|
+
spacing: [],
|
|
80
|
+
fontSizes: [],
|
|
81
|
+
fontWeights: [],
|
|
82
|
+
fontFamilies: [],
|
|
83
|
+
borderRadius: [],
|
|
84
|
+
shadows: [],
|
|
85
|
+
zIndex: [],
|
|
86
|
+
sizing: [],
|
|
87
|
+
lineHeight: [],
|
|
88
|
+
opacity: [],
|
|
89
|
+
transitions: []
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const result = refactorFile(scss, analysis, '/libs/styles/_variables.scss', '/app/test.scss', {});
|
|
93
|
+
|
|
94
|
+
expect(result.modified).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('should not replace values in variable declarations', () => {
|
|
98
|
+
const scss = `
|
|
99
|
+
$my-color: #1976d2;
|
|
100
|
+
.test {
|
|
101
|
+
color: $my-color;
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
const analysis = {
|
|
106
|
+
colors: [
|
|
107
|
+
{ value: '#1976d2', suggestedName: '$color-brand-primary' }
|
|
108
|
+
],
|
|
109
|
+
spacing: [],
|
|
110
|
+
fontSizes: [],
|
|
111
|
+
fontWeights: [],
|
|
112
|
+
fontFamilies: [],
|
|
113
|
+
borderRadius: [],
|
|
114
|
+
shadows: [],
|
|
115
|
+
zIndex: [],
|
|
116
|
+
sizing: [],
|
|
117
|
+
lineHeight: [],
|
|
118
|
+
opacity: [],
|
|
119
|
+
transitions: []
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const result = refactorFile(scss, analysis, '/libs/styles/_variables.scss', '/app/test.scss', {});
|
|
123
|
+
|
|
124
|
+
// Should not replace in variable declaration
|
|
125
|
+
expect(result.content).toContain('$my-color: #1976d2');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const { scanScssFiles } = require('../src/scanner');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
describe('Scanner', () => {
|
|
5
|
+
const fixturesPath = path.join(__dirname, 'fixtures/apps/subapp/src');
|
|
6
|
+
|
|
7
|
+
test('should find SCSS files in directory', async () => {
|
|
8
|
+
const files = await scanScssFiles(fixturesPath);
|
|
9
|
+
|
|
10
|
+
expect(files.length).toBeGreaterThan(0);
|
|
11
|
+
expect(files.every(f => f.endsWith('.scss'))).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('should ignore specified patterns', async () => {
|
|
15
|
+
const files = await scanScssFiles(fixturesPath, ['**/component-b/**']);
|
|
16
|
+
|
|
17
|
+
expect(files.every(f => !f.includes('component-b'))).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('should return absolute paths', async () => {
|
|
21
|
+
const files = await scanScssFiles(fixturesPath);
|
|
22
|
+
|
|
23
|
+
expect(files.every(f => path.isAbsolute(f))).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
});
|