scss-variable-extractor 1.1.0 ā 1.5.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.
- package/README.md +348 -12
- package/bin/cli.js +376 -10
- package/package.json +6 -2
- package/src/angular-parser.js +381 -0
- package/src/bootstrap-migrator.js +634 -0
- package/src/config.js +11 -2
- package/src/index.js +11 -1
- package/src/ng-refactorer.js +578 -0
- package/src/scanner.js +26 -1
- package/test/angular-parser.test.js +230 -0
- package/test/bootstrap-migrator.test.js +213 -0
- package/test/fixtures/angular.json +123 -0
- package/test/fixtures/apps/subapp/src/app/bootstrap-component/bootstrap-component.component.html +36 -0
- package/test/fixtures/apps/subapp/src/app/component-c/component-c.component.scss +47 -0
- package/test/ng-refactorer.test.js +184 -0
- package/test/scanner.test.js +8 -1
package/bin/cli.js
CHANGED
|
@@ -4,11 +4,13 @@ const { Command } = require('commander');
|
|
|
4
4
|
const chalk = require('chalk');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { loadConfig } = require('../src/config');
|
|
7
|
-
const { scanScssFiles } = require('../src/scanner');
|
|
7
|
+
const { scanScssFiles, scanTemplateFiles } = require('../src/scanner');
|
|
8
8
|
const { parseScss } = require('../src/parser');
|
|
9
9
|
const { analyzeValues } = require('../src/analyzer');
|
|
10
10
|
const { generateVariablesFile, generateReport } = require('../src/generator');
|
|
11
11
|
const { refactorScssFiles } = require('../src/refactorer');
|
|
12
|
+
const { analyzeAngularPatterns, refactorAngularPatterns, generateAngularPatternReport } = require('../src/ng-refactorer');
|
|
13
|
+
const { detectBootstrap, migrateBootstrapToMaterial, generateBootstrapReport } = require('../src/bootstrap-migrator');
|
|
12
14
|
|
|
13
15
|
const program = new Command();
|
|
14
16
|
|
|
@@ -21,21 +23,35 @@ program
|
|
|
21
23
|
program
|
|
22
24
|
.command('analyze')
|
|
23
25
|
.description('Dry-run analysis - identifies repeated values without modifying files')
|
|
24
|
-
.argument('
|
|
26
|
+
.argument('[src]', 'Source directory to scan (optional if using angular.json)')
|
|
25
27
|
.option('--threshold <number>', 'Minimum repeat count threshold', parseInt)
|
|
26
28
|
.option('--format <format>', 'Report format (table, json, markdown)', 'table')
|
|
27
29
|
.option('--config <path>', 'Path to config file')
|
|
30
|
+
.option('--angular-json <path>', 'Path to angular.json file')
|
|
31
|
+
.option('--project <name>', 'Angular project name (uses default if not specified)')
|
|
32
|
+
.option('--no-angular', 'Disable angular.json integration')
|
|
28
33
|
.action(async (src, options) => {
|
|
29
34
|
try {
|
|
30
35
|
console.log(chalk.cyan.bold('\nš SCSS Variable Extraction Analysis\n'));
|
|
31
36
|
|
|
32
|
-
const config = loadConfig(options.config
|
|
37
|
+
const config = loadConfig(options.config, {
|
|
38
|
+
useAngularJson: options.angular !== false,
|
|
39
|
+
angularJsonPath: options.angularJson,
|
|
40
|
+
projectName: options.project
|
|
41
|
+
});
|
|
33
42
|
|
|
34
43
|
// Override config with command-line options
|
|
35
|
-
config.src = src;
|
|
44
|
+
if (src) config.src = src;
|
|
36
45
|
if (options.threshold) config.threshold = options.threshold;
|
|
37
46
|
if (options.format) config.reportFormat = options.format;
|
|
38
47
|
|
|
48
|
+
// Show Angular project info if available
|
|
49
|
+
if (config.angular) {
|
|
50
|
+
console.log(chalk.cyan(`Angular Project: ${config.angular.project}`));
|
|
51
|
+
console.log(chalk.gray(`Prefix: ${config.angular.prefix}`));
|
|
52
|
+
console.log(chalk.gray(`Style: ${config.angular.stylePreprocessor}\n`));
|
|
53
|
+
}
|
|
54
|
+
|
|
39
55
|
console.log(chalk.gray(`Scanning: ${config.src}`));
|
|
40
56
|
console.log(chalk.gray(`Threshold: ${config.threshold} occurrences\n`));
|
|
41
57
|
|
|
@@ -85,21 +101,35 @@ program
|
|
|
85
101
|
program
|
|
86
102
|
.command('generate')
|
|
87
103
|
.description('Generate variables file only (does not modify existing SCSS files)')
|
|
88
|
-
.argument('
|
|
104
|
+
.argument('[src]', 'Source directory to scan (optional if using angular.json)')
|
|
89
105
|
.option('--output <path>', 'Output path for variables file')
|
|
90
106
|
.option('--threshold <number>', 'Minimum repeat count threshold', parseInt)
|
|
91
107
|
.option('--config <path>', 'Path to config file')
|
|
108
|
+
.option('--angular-json <path>', 'Path to angular.json file')
|
|
109
|
+
.option('--project <name>', 'Angular project name (uses default if not specified)')
|
|
110
|
+
.option('--no-angular', 'Disable angular.json integration')
|
|
92
111
|
.action(async (src, options) => {
|
|
93
112
|
try {
|
|
94
113
|
console.log(chalk.cyan.bold('\nš Generating SCSS Variables File\n'));
|
|
95
114
|
|
|
96
|
-
const config = loadConfig(options.config
|
|
115
|
+
const config = loadConfig(options.config, {
|
|
116
|
+
useAngularJson: options.angular !== false,
|
|
117
|
+
angularJsonPath: options.angularJson,
|
|
118
|
+
projectName: options.project
|
|
119
|
+
});
|
|
97
120
|
|
|
98
121
|
// Override config with command-line options
|
|
99
|
-
config.src = src;
|
|
122
|
+
if (src) config.src = src;
|
|
100
123
|
if (options.output) config.output = options.output;
|
|
101
124
|
if (options.threshold) config.threshold = options.threshold;
|
|
102
125
|
|
|
126
|
+
// Show Angular project info if available
|
|
127
|
+
if (config.angular) {
|
|
128
|
+
console.log(chalk.cyan(`Angular Project: ${config.angular.project}`));
|
|
129
|
+
console.log(chalk.gray(`Prefix: ${config.angular.prefix}`));
|
|
130
|
+
console.log(chalk.gray(`Style: ${config.angular.stylePreprocessor}\n`));
|
|
131
|
+
}
|
|
132
|
+
|
|
103
133
|
console.log(chalk.gray(`Scanning: ${config.src}`));
|
|
104
134
|
console.log(chalk.gray(`Output: ${config.output}\n`));
|
|
105
135
|
|
|
@@ -154,21 +184,35 @@ program
|
|
|
154
184
|
program
|
|
155
185
|
.command('refactor')
|
|
156
186
|
.description('Full extraction + replacement (generates variables file and refactors SCSS files)')
|
|
157
|
-
.argument('
|
|
187
|
+
.argument('[src]', 'Source directory to scan (optional if using angular.json)')
|
|
158
188
|
.option('--output <path>', 'Output path for variables file')
|
|
159
189
|
.option('--threshold <number>', 'Minimum repeat count threshold', parseInt)
|
|
160
190
|
.option('--config <path>', 'Path to config file')
|
|
191
|
+
.option('--angular-json <path>', 'Path to angular.json file')
|
|
192
|
+
.option('--project <name>', 'Angular project name (uses default if not specified)')
|
|
193
|
+
.option('--no-angular', 'Disable angular.json integration')
|
|
161
194
|
.action(async (src, options) => {
|
|
162
195
|
try {
|
|
163
196
|
console.log(chalk.cyan.bold('\nš§ SCSS Refactoring - Full Extraction\n'));
|
|
164
197
|
|
|
165
|
-
const config = loadConfig(options.config
|
|
198
|
+
const config = loadConfig(options.config, {
|
|
199
|
+
useAngularJson: options.angular !== false,
|
|
200
|
+
angularJsonPath: options.angularJson,
|
|
201
|
+
projectName: options.project
|
|
202
|
+
});
|
|
166
203
|
|
|
167
204
|
// Override config with command-line options
|
|
168
|
-
config.src = src;
|
|
205
|
+
if (src) config.src = src;
|
|
169
206
|
if (options.output) config.output = options.output;
|
|
170
207
|
if (options.threshold) config.threshold = options.threshold;
|
|
171
208
|
|
|
209
|
+
// Show Angular project info if available
|
|
210
|
+
if (config.angular) {
|
|
211
|
+
console.log(chalk.cyan(`Angular Project: ${config.angular.project}`));
|
|
212
|
+
console.log(chalk.gray(`Prefix: ${config.angular.prefix}`));
|
|
213
|
+
console.log(chalk.gray(`Style: ${config.angular.stylePreprocessor}\n`));
|
|
214
|
+
}
|
|
215
|
+
|
|
172
216
|
console.log(chalk.gray(`Scanning: ${config.src}`));
|
|
173
217
|
console.log(chalk.gray(`Output: ${config.output}\n`));
|
|
174
218
|
|
|
@@ -223,4 +267,326 @@ program
|
|
|
223
267
|
}
|
|
224
268
|
});
|
|
225
269
|
|
|
270
|
+
// Analyze Angular patterns command (ng-deep, !important)
|
|
271
|
+
program
|
|
272
|
+
.command('analyze-patterns')
|
|
273
|
+
.description('Analyze SCSS files for Angular Material v15+ anti-patterns (::ng-deep, !important)')
|
|
274
|
+
.argument('[src]', 'Source directory to scan (optional if using angular.json)')
|
|
275
|
+
.option('--format <format>', 'Report format (table, json, markdown)', 'table')
|
|
276
|
+
.option('--config <path>', 'Path to config file')
|
|
277
|
+
.option('--angular-json <path>', 'Path to angular.json file')
|
|
278
|
+
.option('--project <name>', 'Angular project name (uses default if not specified)')
|
|
279
|
+
.option('--no-angular', 'Disable angular.json integration')
|
|
280
|
+
.action(async (src, options) => {
|
|
281
|
+
try {
|
|
282
|
+
console.log(chalk.cyan.bold('\nš Angular Anti-Pattern Analysis\n'));
|
|
283
|
+
|
|
284
|
+
const config = loadConfig(options.config, {
|
|
285
|
+
useAngularJson: options.angular !== false,
|
|
286
|
+
angularJsonPath: options.angularJson,
|
|
287
|
+
projectName: options.project
|
|
288
|
+
});
|
|
289
|
+
if (src) config.src = src;
|
|
290
|
+
|
|
291
|
+
// Show Angular project info if available
|
|
292
|
+
if (config.angular) {
|
|
293
|
+
console.log(chalk.cyan(`Angular Project: ${config.angular.project}`));
|
|
294
|
+
console.log(chalk.gray(`Prefix: ${config.angular.prefix}`));
|
|
295
|
+
console.log(chalk.gray(`Style: ${config.angular.stylePreprocessor}\n`));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
console.log(chalk.gray(`Scanning: ${config.src}\n`));
|
|
299
|
+
|
|
300
|
+
// Scan files
|
|
301
|
+
const files = await scanScssFiles(config.src, config.ignore);
|
|
302
|
+
console.log(chalk.green(`ā Found ${files.length} SCSS files\n`));
|
|
303
|
+
|
|
304
|
+
// Analyze patterns
|
|
305
|
+
const analysis = analyzeAngularPatterns(files);
|
|
306
|
+
|
|
307
|
+
// Generate report
|
|
308
|
+
const report = generateAngularPatternReport(analysis, options.format);
|
|
309
|
+
console.log(report);
|
|
310
|
+
|
|
311
|
+
// Exit with error code if issues found
|
|
312
|
+
if (analysis.summary.ngDeepCount > 0 || analysis.summary.importantCount > 0) {
|
|
313
|
+
console.log(chalk.yellow('ā Anti-patterns detected. Run "modernize" command to fix them.\n'));
|
|
314
|
+
} else {
|
|
315
|
+
console.log(chalk.green('ā No anti-patterns detected!\n'));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.error(chalk.red('ā Error:'), error.message);
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Modernize command (refactor ng-deep and !important)
|
|
325
|
+
program
|
|
326
|
+
.command('modernize')
|
|
327
|
+
.description('Refactor SCSS files following Angular Material v15+ best practices')
|
|
328
|
+
.argument('[src]', 'Source directory to scan (optional if using angular.json)')
|
|
329
|
+
.option('--global-styles <path>', 'Path to global styles file', './src/styles.scss')
|
|
330
|
+
.option('--no-ng-deep', 'Skip ::ng-deep refactoring')
|
|
331
|
+
.option('--no-important', 'Skip !important refactoring')
|
|
332
|
+
.option('--dry-run', 'Preview changes without modifying files')
|
|
333
|
+
.option('--config <path>', 'Path to config file')
|
|
334
|
+
.option('--angular-json <path>', 'Path to angular.json file')
|
|
335
|
+
.option('--project <name>', 'Angular project name (uses default if not specified)')
|
|
336
|
+
.option('--no-angular', 'Disable angular.json integration')
|
|
337
|
+
.action(async (src, options) => {
|
|
338
|
+
try {
|
|
339
|
+
console.log(chalk.cyan.bold('\nš§ Modernizing SCSS Files (Angular Material v15+ Best Practices)\n'));
|
|
340
|
+
|
|
341
|
+
const config = loadConfig(options.config, {
|
|
342
|
+
useAngularJson: options.angular !== false,
|
|
343
|
+
angularJsonPath: options.angularJson,
|
|
344
|
+
projectName: options.project
|
|
345
|
+
});
|
|
346
|
+
if (src) config.src = src;
|
|
347
|
+
|
|
348
|
+
if (options.dryRun) {
|
|
349
|
+
console.log(chalk.yellow('DRY RUN MODE - No files will be modified\n'));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Show Angular project info if available
|
|
353
|
+
if (config.angular) {
|
|
354
|
+
console.log(chalk.cyan(`Angular Project: ${config.angular.project}`));
|
|
355
|
+
console.log(chalk.gray(`Prefix: ${config.angular.prefix}`));
|
|
356
|
+
console.log(chalk.gray(`Style: ${config.angular.stylePreprocessor}\n`));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
console.log(chalk.gray(`Scanning: ${config.src}`));
|
|
360
|
+
console.log(chalk.gray(`Global styles: ${options.globalStyles}\n`));
|
|
361
|
+
|
|
362
|
+
// Scan files
|
|
363
|
+
const files = await scanScssFiles(config.src, config.ignore);
|
|
364
|
+
console.log(chalk.green(`ā Found ${files.length} SCSS files\n`));
|
|
365
|
+
|
|
366
|
+
// Refactor patterns
|
|
367
|
+
const results = refactorAngularPatterns(files, {
|
|
368
|
+
removeNgDeep: options.ngDeep !== false,
|
|
369
|
+
removeImportant: options.important !== false,
|
|
370
|
+
createGlobalStyles: true,
|
|
371
|
+
globalStylesPath: options.globalStyles,
|
|
372
|
+
dryRun: options.dryRun
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Show results
|
|
376
|
+
if (results.modified.length > 0) {
|
|
377
|
+
console.log(chalk.green(`ā Modified ${results.modified.length} file(s)\n`));
|
|
378
|
+
|
|
379
|
+
results.modified.forEach(file => {
|
|
380
|
+
console.log(chalk.white(` ${path.basename(file.file)}:`));
|
|
381
|
+
file.changes.forEach(change => {
|
|
382
|
+
if (change.type === 'ng-deep-removed') {
|
|
383
|
+
console.log(chalk.yellow(` - Removed ${change.count} ::ng-deep occurrence(s)`));
|
|
384
|
+
} else if (change.type === 'important-removed') {
|
|
385
|
+
console.log(chalk.yellow(` - Removed ${change.count} !important declaration(s)`));
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
console.log();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (results.globalStyles.length > 0) {
|
|
393
|
+
console.log(chalk.cyan(`ā Extracted ${results.globalStyles.length} style(s) to ${options.globalStyles}\n`));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (results.warnings.length > 0) {
|
|
397
|
+
console.log(chalk.yellow.bold('ā Warnings:\n'));
|
|
398
|
+
results.warnings.forEach(warning => {
|
|
399
|
+
console.log(chalk.yellow(` ${warning.message}`));
|
|
400
|
+
});
|
|
401
|
+
console.log();
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (results.modified.length === 0) {
|
|
405
|
+
console.log(chalk.green('ā No changes needed - files already follow best practices!\n'));
|
|
406
|
+
} else if (!options.dryRun) {
|
|
407
|
+
console.log(chalk.bold.green('ā Modernization complete!\n'));
|
|
408
|
+
console.log(chalk.gray('Next steps:'));
|
|
409
|
+
console.log(chalk.gray(' 1. Review the modified files'));
|
|
410
|
+
console.log(chalk.gray(' 2. Test your application'));
|
|
411
|
+
console.log(chalk.gray(' 3. Consider using Angular Material theme mixins for component theming'));
|
|
412
|
+
console.log(chalk.gray(' 4. Review extracted global styles and organize as needed\n'));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
} catch (error) {
|
|
416
|
+
console.error(chalk.red('ā Error:'), error.message);
|
|
417
|
+
process.exit(1);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// Detect Bootstrap command
|
|
422
|
+
program
|
|
423
|
+
.command('detect-bootstrap')
|
|
424
|
+
.description('Detect Bootstrap classes and analyze migration to Angular Material')
|
|
425
|
+
.argument('[src]', 'Source directory to scan (optional if using angular.json)')
|
|
426
|
+
.option('--format <format>', 'Report format (table, json, markdown)', 'table')
|
|
427
|
+
.option('--config <path>', 'Path to config file')
|
|
428
|
+
.option('--angular-json <path>', 'Path to angular.json file')
|
|
429
|
+
.option('--project <name>', 'Angular project name (uses default if not specified)')
|
|
430
|
+
.option('--no-angular', 'Disable angular.json integration')
|
|
431
|
+
.action(async (src, options) => {
|
|
432
|
+
try {
|
|
433
|
+
console.log(chalk.cyan.bold('\nš¦ Bootstrap to Material Migration Analysis\n'));
|
|
434
|
+
|
|
435
|
+
const config = loadConfig(options.config, {
|
|
436
|
+
useAngularJson: options.angular !== false,
|
|
437
|
+
angularJsonPath: options.angularJson,
|
|
438
|
+
projectName: options.project
|
|
439
|
+
});
|
|
440
|
+
if (src) config.src = src;
|
|
441
|
+
|
|
442
|
+
// Show Angular project info if available
|
|
443
|
+
if (config.angular) {
|
|
444
|
+
console.log(chalk.cyan(`Angular Project: ${config.angular.project}`));
|
|
445
|
+
console.log(chalk.gray(`Prefix: ${config.angular.prefix}`));
|
|
446
|
+
console.log(chalk.gray(`Style: ${config.angular.stylePreprocessor}\n`));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
console.log(chalk.gray(`Scanning: ${config.src}\n`));
|
|
450
|
+
|
|
451
|
+
// Scan files - look for SCSS, HTML, and TS files
|
|
452
|
+
const allFiles = await scanTemplateFiles(config.src, config.ignore);
|
|
453
|
+
console.log(chalk.green(`ā Found ${allFiles.length} files (SCSS, HTML, TS)\n`));
|
|
454
|
+
|
|
455
|
+
// Detect Bootstrap
|
|
456
|
+
const detection = detectBootstrap(allFiles);
|
|
457
|
+
|
|
458
|
+
// Generate report
|
|
459
|
+
const report = generateBootstrapReport(detection, options.format);
|
|
460
|
+
console.log(report);
|
|
461
|
+
|
|
462
|
+
// Provide migration guidance
|
|
463
|
+
if (detection.summary.totalBootstrapClasses > 0) {
|
|
464
|
+
console.log(chalk.yellow('ā Bootstrap classes detected. Use "migrate-bootstrap" command to convert.\n'));
|
|
465
|
+
console.log(chalk.gray('Migration options:'));
|
|
466
|
+
console.log(chalk.gray(' ⢠Components (btn, card, etc.) ā Angular Material components'));
|
|
467
|
+
console.log(chalk.gray(' ⢠Utilities (d-flex, spacing, etc.) ā Custom utility classes'));
|
|
468
|
+
console.log(chalk.gray(' ⢠Grid system ā CSS Grid or Flexbox\n'));
|
|
469
|
+
} else {
|
|
470
|
+
console.log(chalk.green('ā No Bootstrap classes detected!\n'));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
} catch (error) {
|
|
474
|
+
console.error(chalk.red('ā Error:'), error.message);
|
|
475
|
+
process.exit(1);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
// Migrate Bootstrap command
|
|
480
|
+
program
|
|
481
|
+
.command('migrate-bootstrap')
|
|
482
|
+
.description('Migrate Bootstrap classes to Angular Material MDC-based styles')
|
|
483
|
+
.argument('[src]', 'Source directory to scan (optional if using angular.json)')
|
|
484
|
+
.option('--utilities <path>', 'Path to custom utilities file', './src/styles/utilities.scss')
|
|
485
|
+
.option('--no-custom-utilities', 'Skip creating custom utility classes')
|
|
486
|
+
.option('--no-remove-imports', 'Keep Bootstrap imports')
|
|
487
|
+
.option('--dry-run', 'Preview changes without modifying files')
|
|
488
|
+
.option('--config <path>', 'Path to config file')
|
|
489
|
+
.option('--angular-json <path>', 'Path to angular.json file')
|
|
490
|
+
.option('--project <name>', 'Angular project name (uses default if not specified)')
|
|
491
|
+
.option('--no-angular', 'Disable angular.json integration')
|
|
492
|
+
.action(async (src, options) => {
|
|
493
|
+
try {
|
|
494
|
+
console.log(chalk.cyan.bold('\nš Migrating Bootstrap to Angular Material\n'));
|
|
495
|
+
|
|
496
|
+
const config = loadConfig(options.config, {
|
|
497
|
+
useAngularJson: options.angular !== false,
|
|
498
|
+
angularJsonPath: options.angularJson,
|
|
499
|
+
projectName: options.project
|
|
500
|
+
});
|
|
501
|
+
if (src) config.src = src;
|
|
502
|
+
|
|
503
|
+
if (options.dryRun) {
|
|
504
|
+
console.log(chalk.yellow('DRY RUN MODE - No files will be modified\n'));
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Show Angular project info if available
|
|
508
|
+
if (config.angular) {
|
|
509
|
+
console.log(chalk.cyan(`Angular Project: ${config.angular.project}`));
|
|
510
|
+
console.log(chalk.gray(`Prefix: ${config.angular.prefix}`));
|
|
511
|
+
console.log(chalk.gray(`Style: ${config.angular.stylePreprocessor}\n`));
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
console.log(chalk.gray(`Scanning: ${config.src}`));
|
|
515
|
+
console.log(chalk.gray(`Utilities output: ${options.utilities}\n`));
|
|
516
|
+
|
|
517
|
+
// Scan files - look for SCSS, HTML, and TS files
|
|
518
|
+
const allFiles = await scanTemplateFiles(config.src, config.ignore);
|
|
519
|
+
console.log(chalk.green(`ā Found ${allFiles.length} files (SCSS, HTML, TS)\n`));
|
|
520
|
+
|
|
521
|
+
// Migrate Bootstrap
|
|
522
|
+
const results = migrateBootstrapToMaterial(allFiles, {
|
|
523
|
+
createCustomUtilities: options.customUtilities !== false,
|
|
524
|
+
customUtilitiesPath: options.utilities,
|
|
525
|
+
removeBootstrapImports: options.removeImports !== false,
|
|
526
|
+
dryRun: options.dryRun
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// Show results
|
|
530
|
+
if (results.modified.length > 0) {
|
|
531
|
+
console.log(chalk.green(`ā Modified ${results.modified.length} file(s)\n`));
|
|
532
|
+
|
|
533
|
+
results.modified.slice(0, 10).forEach(file => {
|
|
534
|
+
console.log(chalk.white(` ${path.basename(file.file)}:`));
|
|
535
|
+
file.changes.forEach(change => {
|
|
536
|
+
if (change.type === 'import-removed') {
|
|
537
|
+
console.log(chalk.yellow(` - ${change.description}`));
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
if (results.modified.length > 10) {
|
|
542
|
+
console.log(chalk.gray(` ... and ${results.modified.length - 10} more`));
|
|
543
|
+
}
|
|
544
|
+
console.log();
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (results.customUtilities.length > 0) {
|
|
548
|
+
console.log(chalk.cyan(`ā Generated ${results.customUtilities.length} custom utility class(es) in ${options.utilities}\n`));
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (results.componentMigrations.length > 0) {
|
|
552
|
+
console.log(chalk.yellow(`ā ${results.componentMigrations.length} component(s) require manual migration\n`));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (results.warnings.length > 0) {
|
|
556
|
+
console.log(chalk.yellow.bold('ā Warnings:\n'));
|
|
557
|
+
results.warnings.slice(0, 5).forEach(warning => {
|
|
558
|
+
console.log(chalk.yellow(` ${warning.message}`));
|
|
559
|
+
if (warning.variables) {
|
|
560
|
+
console.log(chalk.gray(` Variables: ${warning.variables.join(', ')}`));
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
if (results.warnings.length > 5) {
|
|
564
|
+
console.log(chalk.gray(` ... and ${results.warnings.length - 5} more warnings`));
|
|
565
|
+
}
|
|
566
|
+
console.log();
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (results.modified.length === 0 && results.warnings.length === 0) {
|
|
570
|
+
console.log(chalk.green('ā No Bootstrap code detected!\n'));
|
|
571
|
+
} else if (!options.dryRun) {
|
|
572
|
+
console.log(chalk.bold.green('ā Migration complete!\n'));
|
|
573
|
+
console.log(chalk.gray('Next steps:'));
|
|
574
|
+
console.log(chalk.gray(' 1. Review the modified SCSS files'));
|
|
575
|
+
console.log(chalk.gray(' 2. Update HTML templates to use Angular Material components'));
|
|
576
|
+
console.log(chalk.gray(' 3. Import custom utilities in your styles.scss'));
|
|
577
|
+
console.log(chalk.gray(' 4. Install @angular/material if not already installed'));
|
|
578
|
+
console.log(chalk.gray(' 5. Test your application thoroughly\n'));
|
|
579
|
+
console.log(chalk.cyan('Component Migration Guide:'));
|
|
580
|
+
console.log(chalk.gray(' ⢠.btn ā <button mat-button>'));
|
|
581
|
+
console.log(chalk.gray(' ⢠.card ā <mat-card>'));
|
|
582
|
+
console.log(chalk.gray(' ⢠.form-control ā <mat-form-field><input matInput></mat-form-field>'));
|
|
583
|
+
console.log(chalk.gray(' ⢠See: https://material.angular.io/components\n'));
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
} catch (error) {
|
|
587
|
+
console.error(chalk.red('ā Error:'), error.message);
|
|
588
|
+
process.exit(1);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
|
|
226
592
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scss-variable-extractor",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.5.1",
|
|
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"
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
"test": "jest",
|
|
11
11
|
"analyze": "node bin/cli.js analyze .",
|
|
12
12
|
"generate": "node bin/cli.js generate .",
|
|
13
|
-
"refactor": "node bin/cli.js refactor ."
|
|
13
|
+
"refactor": "node bin/cli.js refactor .",
|
|
14
|
+
"analyze-patterns": "node bin/cli.js analyze-patterns .",
|
|
15
|
+
"modernize": "node bin/cli.js modernize .",
|
|
16
|
+
"detect-bootstrap": "node bin/cli.js detect-bootstrap .",
|
|
17
|
+
"migrate-bootstrap": "node bin/cli.js migrate-bootstrap ."
|
|
14
18
|
},
|
|
15
19
|
"keywords": [
|
|
16
20
|
"angular",
|