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.
@@ -0,0 +1,35 @@
1
+ {
2
+ "src": "./apps/subapp/src",
3
+ "output": "./libs/styles/_variables.scss",
4
+ "threshold": 2,
5
+ "categories": {
6
+ "colors": true,
7
+ "spacing": true,
8
+ "fontSizes": true,
9
+ "fontWeights": true,
10
+ "fontFamilies": true,
11
+ "borderRadius": true,
12
+ "shadows": true,
13
+ "zIndex": true,
14
+ "sizing": true,
15
+ "lineHeight": true,
16
+ "opacity": true,
17
+ "transitions": true
18
+ },
19
+ "spacingScale": {
20
+ "xxs": "2px",
21
+ "xs": "4px",
22
+ "sm": "8px",
23
+ "md": "16px",
24
+ "lg": "24px",
25
+ "xl": "32px",
26
+ "xxl": "48px"
27
+ },
28
+ "ignore": [
29
+ "**/node_modules/**",
30
+ "**/dist/**",
31
+ "**/_variables.scss"
32
+ ],
33
+ "importStyle": "use",
34
+ "reportFormat": "table"
35
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SCSS Variable Extractor Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,423 @@
1
+ # SCSS Variable Extractor
2
+
3
+ A standalone, reusable CLI tool that analyzes Angular project SCSS files, identifies repeated hardcoded values, extracts them into SCSS variables, and refactors the codebase to use those variables.
4
+
5
+ ## Features
6
+
7
+ ✨ **Automated Variable Extraction** - Identifies and extracts repeated hardcoded values
8
+ šŸŽÆ **Angular 15 & Material 15 Ready** - Follows best practices with `@use` imports
9
+ šŸ” **Smart Pattern Recognition** - Detects colors, spacing, fonts, shadows, and more
10
+ šŸ“Š **Multiple Report Formats** - Table, JSON, or Markdown output
11
+ šŸ›”ļø **Safe Refactoring** - Preserves existing variables and avoids unsafe replacements
12
+ āš™ļø **Highly Configurable** - Customize via `.scssextractrc.json`
13
+
14
+ ## Installation
15
+
16
+ ### From Git Repository
17
+
18
+ ```bash
19
+ npm install git+https://github.com/lpaszkiewicz/scss-variable-extractor.git
20
+ ```
21
+
22
+ ### As a Git Submodule
23
+
24
+ ```bash
25
+ git submodule add https://github.com/lpaszkiewicz/scss-variable-extractor.git tools/scss-extract
26
+ cd tools/scss-extract
27
+ npm install
28
+ ```
29
+
30
+ ### Local Development
31
+
32
+ ```bash
33
+ git clone https://github.com/lpaszkiewicz/scss-variable-extractor.git
34
+ cd scss-variable-extractor
35
+ npm install
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ### 1. Analyze (Dry Run)
41
+
42
+ See what values would be extracted without modifying any files:
43
+
44
+ ```bash
45
+ npx scss-extract analyze --src ./apps/subapp/src --threshold 2
46
+ ```
47
+
48
+ ### 2. Generate Variables File
49
+
50
+ Create a `_variables.scss` file with extracted variables:
51
+
52
+ ```bash
53
+ npx scss-extract generate --src ./apps/subapp/src --output ./libs/styles/_variables.scss
54
+ ```
55
+
56
+ ### 3. Full Refactoring
57
+
58
+ Generate variables file AND replace hardcoded values in all SCSS files:
59
+
60
+ ```bash
61
+ npx scss-extract refactor --src ./apps/subapp/src --output ./libs/styles/_variables.scss
62
+ ```
63
+
64
+ ## CLI Commands
65
+
66
+ ### `analyze` - Dry-run Analysis
67
+
68
+ Scans SCSS files and reports repeated values without modifying anything.
69
+
70
+ ```bash
71
+ npx scss-extract analyze [options]
72
+ ```
73
+
74
+ **Options:**
75
+ - `--src <path>` - Source directory to scan (default: `./apps/subapp/src`)
76
+ - `--threshold <number>` - Minimum repeat count (default: `2`)
77
+ - `--format <format>` - Report format: `table`, `json`, `markdown` (default: `table`)
78
+ - `--config <path>` - Path to custom config file
79
+
80
+ **Example:**
81
+ ```bash
82
+ npx scss-extract analyze --src ./src --threshold 3 --format markdown
83
+ ```
84
+
85
+ ### `generate` - Generate Variables File
86
+
87
+ Creates a `_variables.scss` file with extracted variables.
88
+
89
+ ```bash
90
+ npx scss-extract generate [options]
91
+ ```
92
+
93
+ **Options:**
94
+ - `--src <path>` - Source directory to scan
95
+ - `--output <path>` - Output path for variables file (default: `./libs/styles/_variables.scss`)
96
+ - `--threshold <number>` - Minimum repeat count
97
+ - `--config <path>` - Path to custom config file
98
+
99
+ **Example:**
100
+ ```bash
101
+ npx scss-extract generate --src ./apps/myapp/src --output ./styles/_variables.scss
102
+ ```
103
+
104
+ ### `refactor` - Full Extraction + Replacement
105
+
106
+ Generates variables file AND refactors SCSS files to use variables.
107
+
108
+ ```bash
109
+ npx scss-extract refactor [options]
110
+ ```
111
+
112
+ **Options:**
113
+ - `--src <path>` - Source directory to scan
114
+ - `--output <path>` - Output path for variables file
115
+ - `--threshold <number>` - Minimum repeat count
116
+ - `--config <path>` - Path to custom config file
117
+
118
+ **Example:**
119
+ ```bash
120
+ npx scss-extract refactor --src ./apps/subapp/src --output ./libs/styles/_variables.scss
121
+ ```
122
+
123
+ ## Configuration
124
+
125
+ Create a `.scssextractrc.json` file in your project root:
126
+
127
+ ```json
128
+ {
129
+ "src": "./apps/subapp/src",
130
+ "output": "./libs/styles/_variables.scss",
131
+ "threshold": 2,
132
+ "categories": {
133
+ "colors": true,
134
+ "spacing": true,
135
+ "fontSizes": true,
136
+ "fontWeights": true,
137
+ "fontFamilies": true,
138
+ "borderRadius": true,
139
+ "shadows": true,
140
+ "zIndex": true,
141
+ "sizing": true,
142
+ "lineHeight": true,
143
+ "opacity": true,
144
+ "transitions": true
145
+ },
146
+ "spacingScale": {
147
+ "xxs": "2px",
148
+ "xs": "4px",
149
+ "sm": "8px",
150
+ "md": "16px",
151
+ "lg": "24px",
152
+ "xl": "32px",
153
+ "xxl": "48px"
154
+ },
155
+ "ignore": [
156
+ "**/node_modules/**",
157
+ "**/dist/**",
158
+ "**/_variables.scss"
159
+ ],
160
+ "importStyle": "use",
161
+ "reportFormat": "table"
162
+ }
163
+ ```
164
+
165
+ See [`.scssextractrc.example.json`](./.scssextractrc.example.json) for a complete example.
166
+
167
+ ## Value Detection Categories
168
+
169
+ The tool detects and categorizes these types of repeated values:
170
+
171
+ | Category | Prefix | Examples |
172
+ |----------|--------|----------|
173
+ | **Colors** | `$color-` | `#1976d2`, `rgb(25, 118, 210)`, `rgba(0,0,0,0.87)`, `white` |
174
+ | **Spacing** | `$spacing-` | `4px`, `8px`, `16px`, `24px` (mapped to t-shirt sizes) |
175
+ | **Font Sizes** | `$font-size-` | `12px`, `14px`, `16px`, `20px` |
176
+ | **Font Weights** | `$font-weight-` | `400`, `500`, `700`, `bold` |
177
+ | **Font Families** | `$font-family-` | `'Roboto', sans-serif` |
178
+ | **Border Radius** | `$border-radius-` | `4px`, `8px`, `50%` |
179
+ | **Box Shadows** | `$shadow-` | `0 2px 4px rgba(0,0,0,0.1)` |
180
+ | **Z-Index** | `$z-index-` | `100`, `1000`, `9999` |
181
+ | **Sizing** | `$size-` | Width/height pixel values |
182
+ | **Line Height** | `$line-height-` | `1.5`, `1.25`, `24px` |
183
+ | **Opacity** | `$opacity-` | `0.5`, `0.87` |
184
+ | **Transitions** | `$transition-` | `all 0.3s ease` |
185
+
186
+ ## How It Works
187
+
188
+ 1. **Scanning** - Recursively finds all `.scss` files in the specified directory
189
+ 2. **Parsing** - Extracts hardcoded values using regex patterns
190
+ 3. **Analyzing** - Counts occurrences and groups by category
191
+ 4. **Generating** - Creates `_variables.scss` with smart variable names
192
+ 5. **Refactoring** - Replaces hardcoded values with `$variable` references
193
+
194
+ ## Variable Naming Convention
195
+
196
+ The tool uses intelligent naming based on value patterns and context:
197
+
198
+ - **Kebab-case** for all variable names
199
+ - **Category prefixes** (e.g., `$color-`, `$spacing-`)
200
+ - **Semantic names** when possible:
201
+ - `#1976d2` → `$color-brand-primary`
202
+ - `rgba(0,0,0,0.87)` → `$color-text-primary`
203
+ - `16px` (padding) → `$spacing-md`
204
+ - `14px` (font-size) → `$font-size-sm`
205
+ - `500` (font-weight) → `$font-weight-medium`
206
+
207
+ ### Spacing Scale (T-shirt Sizing)
208
+
209
+ | Variable | Value |
210
+ |----------|-------|
211
+ | `$spacing-xxs` | `2px` |
212
+ | `$spacing-xs` | `4px` |
213
+ | `$spacing-sm` | `8px` |
214
+ | `$spacing-md` | `16px` |
215
+ | `$spacing-lg` | `24px` |
216
+ | `$spacing-xl` | `32px` |
217
+ | `$spacing-xxl` | `48px` |
218
+
219
+ ## Safe Refactoring Rules
220
+
221
+ The tool **WILL NOT** replace values in these contexts:
222
+
223
+ āœ… Already-defined `$variable` declarations
224
+ āœ… Values inside `url()` functions
225
+ āœ… Values inside `content:` property
226
+ āœ… Values inside string interpolation `#{}`
227
+ āœ… Values inside `@use` or `@forward` statements
228
+ āœ… Values inside comments
229
+ āœ… Values in Angular Material mixin calls (e.g., `mat.define-palette()`)
230
+ āœ… Single-use values (below the threshold)
231
+
232
+ ## Angular 15 + Material 15 Integration
233
+
234
+ The tool is designed for Angular 15 and Angular Material 15 projects:
235
+
236
+ - ✨ Uses `@use` syntax (not deprecated `@import`)
237
+ - šŸŽØ Recognizes Material theming patterns
238
+ - šŸ›”ļø Preserves Material palette functions:
239
+ - `mat.define-palette()`
240
+ - `mat.get-color-from-palette()`
241
+ - `mat.define-light-theme()` / `mat.define-dark-theme()`
242
+ - `mat.all-component-themes()`
243
+
244
+ ### Generated Variables File
245
+
246
+ ```scss
247
+ //
248
+ // Auto-generated SCSS Variables
249
+ // Generated by scss-variable-extractor v1.0.0
250
+ // Generated at: 2026-02-12T13:00:00.000Z
251
+ //
252
+ // DO NOT EDIT THIS FILE MANUALLY
253
+ // This file is auto-generated. Your changes will be overwritten.
254
+ //
255
+
256
+ // Colors
257
+ // ────────────────────────────────────────
258
+ $color-brand-primary: #1976d2;
259
+ $color-text-primary: rgba(0, 0, 0, 0.87);
260
+ $color-white: white;
261
+
262
+ // Spacing
263
+ // ────────────────────────────────────────
264
+ $spacing-sm: 8px;
265
+ $spacing-md: 16px;
266
+ $spacing-lg: 24px;
267
+
268
+ // Font Sizes
269
+ // ────────────────────────────────────────
270
+ $font-size-xs: 12px;
271
+ $font-size-sm: 14px;
272
+ $font-size-xl: 20px;
273
+ ```
274
+
275
+ ### Refactored Component Example
276
+
277
+ **Before:**
278
+ ```scss
279
+ .component-a {
280
+ background-color: #1976d2;
281
+ color: rgba(0, 0, 0, 0.87);
282
+ padding: 16px;
283
+ font-size: 14px;
284
+ border-radius: 4px;
285
+ }
286
+ ```
287
+
288
+ **After:**
289
+ ```scss
290
+ @use '../../../libs/styles/variables' as *;
291
+
292
+ .component-a {
293
+ background-color: $color-brand-primary;
294
+ color: $color-text-primary;
295
+ padding: $spacing-md;
296
+ font-size: $font-size-sm;
297
+ border-radius: $border-radius-sm;
298
+ }
299
+ ```
300
+
301
+ ## Output Reports
302
+
303
+ ### Console Table (default)
304
+
305
+ ```
306
+ Category | Value | Count | Files | Suggested Variable
307
+ ─────────────|────────────────────|───────|───────|─────────────────────
308
+ color | #1976d2 | 5 | 3 | $color-brand-primary
309
+ color | rgba(0,0,0,0.87) | 8 | 6 | $color-text-primary
310
+ spacing | 16px | 12 | 8 | $spacing-md
311
+ font-size | 14px | 7 | 5 | $font-size-sm
312
+ ```
313
+
314
+ ### JSON (`--format json`)
315
+
316
+ ```json
317
+ {
318
+ "colors": [
319
+ {
320
+ "value": "#1976d2",
321
+ "count": 5,
322
+ "fileCount": 3,
323
+ "suggestedName": "$color-brand-primary"
324
+ }
325
+ ]
326
+ }
327
+ ```
328
+
329
+ ### Markdown (`--format markdown`)
330
+
331
+ ```markdown
332
+ ## Colors
333
+
334
+ | Value | Count | Files | Suggested Variable |
335
+ |-------|-------|-------|-------------------|
336
+ | #1976d2 | 5 | 3 | `$color-brand-primary` |
337
+ ```
338
+
339
+ ## Testing
340
+
341
+ Run the test suite:
342
+
343
+ ```bash
344
+ npm test
345
+ ```
346
+
347
+ Run tests with coverage:
348
+
349
+ ```bash
350
+ npm test -- --coverage
351
+ ```
352
+
353
+ ## Project Structure
354
+
355
+ ```
356
+ scss-variable-extractor/
357
+ ā”œā”€ā”€ bin/
358
+ │ └── cli.js # CLI entry point
359
+ ā”œā”€ā”€ src/
360
+ │ ā”œā”€ā”€ scanner.js # Finds .scss files
361
+ │ ā”œā”€ā”€ parser.js # Extracts hardcoded values
362
+ │ ā”œā”€ā”€ analyzer.js # Identifies repeated values
363
+ │ ā”œā”€ā”€ generator.js # Generates _variables.scss
364
+ │ ā”œā”€ā”€ refactorer.js # Replaces values with variables
365
+ │ ā”œā”€ā”€ config.js # Configuration management
366
+ │ └── index.js # Main exports
367
+ ā”œā”€ā”€ templates/
368
+ │ └── _variables.scss.template # Variables file template
369
+ ā”œā”€ā”€ test/
370
+ │ ā”œā”€ā”€ fixtures/ # Sample SCSS files
371
+ │ ā”œā”€ā”€ scanner.test.js
372
+ │ ā”œā”€ā”€ parser.test.js
373
+ │ ā”œā”€ā”€ analyzer.test.js
374
+ │ ā”œā”€ā”€ generator.test.js
375
+ │ └── refactorer.test.js
376
+ ā”œā”€ā”€ .scssextractrc.example.json # Example config
377
+ ā”œā”€ā”€ package.json
378
+ ā”œā”€ā”€ README.md
379
+ └── LICENSE
380
+ ```
381
+
382
+ ## Requirements
383
+
384
+ - **Node.js** >= 14.0.0
385
+ - **npm** >= 6.0.0
386
+
387
+ ## Contributing
388
+
389
+ Contributions are welcome! Please follow these guidelines:
390
+
391
+ 1. Fork the repository
392
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
393
+ 3. Make your changes
394
+ 4. Add tests for new functionality
395
+ 5. Ensure all tests pass (`npm test`)
396
+ 6. Commit your changes (`git commit -m 'Add amazing feature'`)
397
+ 7. Push to the branch (`git push origin feature/amazing-feature`)
398
+ 8. Open a Pull Request
399
+
400
+ ## License
401
+
402
+ MIT License - see [LICENSE](./LICENSE) file for details
403
+
404
+ ## Support
405
+
406
+ - šŸ› [Report a bug](https://github.com/lpaszkiewicz/scss-variable-extractor/issues)
407
+ - šŸ’” [Request a feature](https://github.com/lpaszkiewicz/scss-variable-extractor/issues)
408
+ - šŸ“– [Documentation](https://github.com/lpaszkiewicz/scss-variable-extractor)
409
+
410
+ ## Changelog
411
+
412
+ ### 1.0.0 (2026-02-12)
413
+
414
+ - Initial release
415
+ - Support for 12 value categories
416
+ - Angular 15 & Material 15 compatibility
417
+ - Three CLI commands: analyze, generate, refactor
418
+ - Multiple report formats
419
+ - Comprehensive test coverage
420
+
421
+ ---
422
+
423
+ Made with ā¤ļø for the Angular community
package/bin/cli.js ADDED
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require('commander');
4
+ const chalk = require('chalk');
5
+ const path = require('path');
6
+ const { loadConfig } = require('../src/config');
7
+ const { scanScssFiles } = require('../src/scanner');
8
+ const { parseScss } = require('../src/parser');
9
+ const { analyzeValues } = require('../src/analyzer');
10
+ const { generateVariablesFile, generateReport } = require('../src/generator');
11
+ const { refactorScssFiles } = require('../src/refactorer');
12
+
13
+ const program = new Command();
14
+
15
+ program
16
+ .name('scss-extract')
17
+ .description('Analyzes Angular SCSS files and extracts repeated hardcoded values into reusable variables')
18
+ .version('1.0.0');
19
+
20
+ // Analyze command
21
+ program
22
+ .command('analyze')
23
+ .description('Dry-run analysis - identifies repeated values without modifying files')
24
+ .option('--src <path>', 'Source directory to scan')
25
+ .option('--threshold <number>', 'Minimum repeat count threshold', parseInt)
26
+ .option('--format <format>', 'Report format (table, json, markdown)', 'table')
27
+ .option('--config <path>', 'Path to config file')
28
+ .action(async (options) => {
29
+ try {
30
+ console.log(chalk.cyan.bold('\nšŸ” SCSS Variable Extraction Analysis\n'));
31
+
32
+ const config = loadConfig(options.config);
33
+
34
+ // Override config with command-line options
35
+ if (options.src) config.src = options.src;
36
+ if (options.threshold) config.threshold = options.threshold;
37
+ if (options.format) config.reportFormat = options.format;
38
+
39
+ console.log(chalk.gray(`Scanning: ${config.src}`));
40
+ console.log(chalk.gray(`Threshold: ${config.threshold} occurrences\n`));
41
+
42
+ // Scan files
43
+ const files = await scanScssFiles(config.src, config.ignore);
44
+ console.log(chalk.green(`āœ“ Found ${files.length} SCSS files\n`));
45
+
46
+ // Parse all files
47
+ const allExtracted = {
48
+ colors: [],
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
+ files.forEach(file => {
63
+ const content = require('fs').readFileSync(file, 'utf8');
64
+ const extracted = parseScss(content, file);
65
+
66
+ Object.keys(extracted).forEach(category => {
67
+ allExtracted[category].push(...extracted[category]);
68
+ });
69
+ });
70
+
71
+ // Analyze
72
+ const analysis = analyzeValues(allExtracted, config);
73
+
74
+ // Generate report
75
+ const report = generateReport(analysis, config.reportFormat, config);
76
+ console.log(report);
77
+
78
+ } catch (error) {
79
+ console.error(chalk.red('āœ— Error:'), error.message);
80
+ process.exit(1);
81
+ }
82
+ });
83
+
84
+ // Generate command
85
+ program
86
+ .command('generate')
87
+ .description('Generate variables file only (does not modify existing SCSS files)')
88
+ .option('--src <path>', 'Source directory to scan')
89
+ .option('--output <path>', 'Output path for variables file')
90
+ .option('--threshold <number>', 'Minimum repeat count threshold', parseInt)
91
+ .option('--config <path>', 'Path to config file')
92
+ .action(async (options) => {
93
+ try {
94
+ console.log(chalk.cyan.bold('\nšŸ“ Generating SCSS Variables File\n'));
95
+
96
+ const config = loadConfig(options.config);
97
+
98
+ // Override config with command-line options
99
+ if (options.src) config.src = options.src;
100
+ if (options.output) config.output = options.output;
101
+ if (options.threshold) config.threshold = options.threshold;
102
+
103
+ console.log(chalk.gray(`Scanning: ${config.src}`));
104
+ console.log(chalk.gray(`Output: ${config.output}\n`));
105
+
106
+ // Scan files
107
+ const files = await scanScssFiles(config.src, config.ignore);
108
+ console.log(chalk.green(`āœ“ Found ${files.length} SCSS files`));
109
+
110
+ // Parse all files
111
+ const allExtracted = {
112
+ colors: [],
113
+ spacing: [],
114
+ fontSizes: [],
115
+ fontWeights: [],
116
+ fontFamilies: [],
117
+ borderRadius: [],
118
+ shadows: [],
119
+ zIndex: [],
120
+ sizing: [],
121
+ lineHeight: [],
122
+ opacity: [],
123
+ transitions: []
124
+ };
125
+
126
+ files.forEach(file => {
127
+ const content = require('fs').readFileSync(file, 'utf8');
128
+ const extracted = parseScss(content, file);
129
+
130
+ Object.keys(extracted).forEach(category => {
131
+ allExtracted[category].push(...extracted[category]);
132
+ });
133
+ });
134
+
135
+ // Analyze
136
+ const analysis = analyzeValues(allExtracted, config);
137
+
138
+ // Generate variables file
139
+ generateVariablesFile(analysis, config.output, config);
140
+
141
+ console.log(chalk.green(`\nāœ“ Generated variables file: ${config.output}`));
142
+
143
+ // Show summary
144
+ const totalVars = Object.values(analysis).reduce((sum, arr) => sum + arr.length, 0);
145
+ console.log(chalk.bold(`\nTotal variables extracted: ${totalVars}\n`));
146
+
147
+ } catch (error) {
148
+ console.error(chalk.red('āœ— Error:'), error.message);
149
+ process.exit(1);
150
+ }
151
+ });
152
+
153
+ // Refactor command
154
+ program
155
+ .command('refactor')
156
+ .description('Full extraction + replacement (generates variables file and refactors SCSS files)')
157
+ .option('--src <path>', 'Source directory to scan')
158
+ .option('--output <path>', 'Output path for variables file')
159
+ .option('--threshold <number>', 'Minimum repeat count threshold', parseInt)
160
+ .option('--config <path>', 'Path to config file')
161
+ .action(async (options) => {
162
+ try {
163
+ console.log(chalk.cyan.bold('\nšŸ”§ SCSS Refactoring - Full Extraction\n'));
164
+
165
+ const config = loadConfig(options.config);
166
+
167
+ // Override config with command-line options
168
+ if (options.src) config.src = options.src;
169
+ if (options.output) config.output = options.output;
170
+ if (options.threshold) config.threshold = options.threshold;
171
+
172
+ console.log(chalk.gray(`Scanning: ${config.src}`));
173
+ console.log(chalk.gray(`Output: ${config.output}\n`));
174
+
175
+ // Scan files
176
+ const files = await scanScssFiles(config.src, config.ignore);
177
+ console.log(chalk.green(`āœ“ Found ${files.length} SCSS files`));
178
+
179
+ // Parse all files
180
+ const allExtracted = {
181
+ colors: [],
182
+ spacing: [],
183
+ fontSizes: [],
184
+ fontWeights: [],
185
+ fontFamilies: [],
186
+ borderRadius: [],
187
+ shadows: [],
188
+ zIndex: [],
189
+ sizing: [],
190
+ lineHeight: [],
191
+ opacity: [],
192
+ transitions: []
193
+ };
194
+
195
+ files.forEach(file => {
196
+ const content = require('fs').readFileSync(file, 'utf8');
197
+ const extracted = parseScss(content, file);
198
+
199
+ Object.keys(extracted).forEach(category => {
200
+ allExtracted[category].push(...extracted[category]);
201
+ });
202
+ });
203
+
204
+ // Analyze
205
+ const analysis = analyzeValues(allExtracted, config);
206
+
207
+ // Generate variables file
208
+ generateVariablesFile(analysis, config.output, config);
209
+ console.log(chalk.green(`āœ“ Generated variables file: ${config.output}`));
210
+
211
+ // Refactor files
212
+ const refactoredFiles = refactorScssFiles(files, analysis, config.output, config);
213
+ console.log(chalk.green(`āœ“ Refactored ${refactoredFiles.length} SCSS files`));
214
+
215
+ // Show summary
216
+ const totalVars = Object.values(analysis).reduce((sum, arr) => sum + arr.length, 0);
217
+ console.log(chalk.bold(`\nTotal variables extracted: ${totalVars}`));
218
+ console.log(chalk.bold(`Files modified: ${refactoredFiles.length}\n`));
219
+
220
+ } catch (error) {
221
+ console.error(chalk.red('āœ— Error:'), error.message);
222
+ process.exit(1);
223
+ }
224
+ });
225
+
226
+ program.parse();