lwc-convert 1.0.1 → 1.1.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.
Files changed (43) hide show
  1. package/README.md +41 -15
  2. package/dist/cli/commands/grade.d.ts +2 -0
  3. package/dist/cli/commands/grade.d.ts.map +1 -0
  4. package/dist/cli/commands/grade.js +211 -0
  5. package/dist/cli/commands/grade.js.map +1 -0
  6. package/dist/cli/interactive.d.ts +9 -6
  7. package/dist/cli/interactive.d.ts.map +1 -1
  8. package/dist/cli/interactive.js +124 -27
  9. package/dist/cli/interactive.js.map +1 -1
  10. package/dist/cli/options.d.ts +1 -1
  11. package/dist/cli/options.d.ts.map +1 -1
  12. package/dist/cli/options.js +14 -1
  13. package/dist/cli/options.js.map +1 -1
  14. package/dist/grading/aura-grader.d.ts +14 -0
  15. package/dist/grading/aura-grader.d.ts.map +1 -0
  16. package/dist/grading/aura-grader.js +158 -0
  17. package/dist/grading/aura-grader.js.map +1 -0
  18. package/dist/grading/complexity-metrics.d.ts +23 -0
  19. package/dist/grading/complexity-metrics.d.ts.map +1 -0
  20. package/dist/grading/complexity-metrics.js +40 -0
  21. package/dist/grading/complexity-metrics.js.map +1 -0
  22. package/dist/grading/grade-calculator.d.ts +8 -0
  23. package/dist/grading/grade-calculator.d.ts.map +1 -0
  24. package/dist/grading/grade-calculator.js +42 -0
  25. package/dist/grading/grade-calculator.js.map +1 -0
  26. package/dist/grading/grader.d.ts +14 -0
  27. package/dist/grading/grader.d.ts.map +1 -0
  28. package/dist/grading/grader.js +189 -0
  29. package/dist/grading/grader.js.map +1 -0
  30. package/dist/grading/types.d.ts +67 -0
  31. package/dist/grading/types.d.ts.map +1 -0
  32. package/dist/grading/types.js +3 -0
  33. package/dist/grading/types.js.map +1 -0
  34. package/dist/grading/vf-grader.d.ts +14 -0
  35. package/dist/grading/vf-grader.d.ts.map +1 -0
  36. package/dist/grading/vf-grader.js +155 -0
  37. package/dist/grading/vf-grader.js.map +1 -0
  38. package/dist/index.js +39 -8
  39. package/dist/index.js.map +1 -1
  40. package/dist/utils/logger.d.ts.map +1 -1
  41. package/dist/utils/logger.js +1 -2
  42. package/dist/utils/logger.js.map +1 -1
  43. package/package.json +5 -5
package/README.md CHANGED
@@ -7,20 +7,6 @@
7
7
  <a href="#-features">Features</a> •
8
8
  <a href="#-conversion-mappings">Mappings</a> •
9
9
  <a href="#-cli-reference">CLI Reference</a> •
10
- <a href="#-contributing">Contributing</a>
11
- </p>
12
-
13
- ---
14
-
15
- [![Node.js](https://img.shields.io/badge/Node.js-18%2B-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
16
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
17
- [![License](https://img.shields.io/badge/License-MIT-yellow)](LICENSE)
18
- [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/Lastonedown86/lwc-convert/pulls)
19
-
20
- ---
21
-
22
- ## 🎯 Overview
23
-
24
10
  `lwc-convert` automates the migration of legacy Salesforce UI technologies to modern Lightning Web Components:
25
11
 
26
12
  | Source Technology | Target | Confidence |
@@ -46,6 +32,7 @@ myComponent/
46
32
  ## ✨ Features
47
33
 
48
34
  ### 🎯 Single-Component Focus
35
+
49
36
  Unlike batch tools that sacrifice accuracy, `lwc-convert` processes **one component at a time** for maximum precision and detailed error reporting.
50
37
 
51
38
  ### 📝 Two Output Modes
@@ -56,13 +43,17 @@ Unlike batch tools that sacrifice accuracy, `lwc-convert` processes **one compon
56
43
  | **Full Conversion** (`--full`) | Complete transformation with `// REVIEW:` markers | Simple, standard components |
57
44
 
58
45
  ### 🔍 Smart Analysis
46
+
59
47
  - Parses Aura markup, controllers, helpers, and styles
60
48
  - Analyzes Apex controllers for VF pages
61
49
  - Detects patterns and suggests modern equivalents
62
50
  - Identifies potential issues upfront
51
+ - **New!** 📊 **Complexity Grading**: Analyze components before conversion to estimate effort and identify risks.
63
52
 
64
53
  ### 📋 Conversion Notes
54
+
65
55
  Every conversion includes a detailed markdown file with:
56
+
66
57
  - ✅ Completed transformations
67
58
  - ⚠️ Items needing manual attention
68
59
  - 📖 Migration guidance and best practices
@@ -80,6 +71,7 @@ npx lwc-convert
80
71
  ```
81
72
 
82
73
  You'll be guided through:
74
+
83
75
  1. **Select conversion type** (Aura or Visualforce)
84
76
  2. **Choose component** from auto-discovered list or enter path
85
77
  3. **Configure options** (scaffolding/full, output dir, open folder)
@@ -147,7 +139,18 @@ lwc-convert vf ./pages/ContactList.page --controller ./classes/ContactListContro
147
139
  lwc-convert aura MyComponent --dry-run --verbose
148
140
  ```
149
141
 
142
+ **Assess Conversion Complexity:**
143
+
144
+ ```bash
145
+ # Grade a single component
146
+ lwc-convert grade AccountCard --type aura
147
+
148
+ # Scan entire project and export report
149
+ lwc-convert grade --type both --format json --output report.json
150
+ ```
151
+
150
152
  > **💡 Smart Path Resolution:** The CLI automatically searches common Salesforce project locations:
153
+ >
151
154
  > - `force-app/main/default/aura/`, `src/aura/`, `aura/`
152
155
  > - `force-app/main/default/pages/`, `src/pages/`, `pages/`
153
156
  > - `force-app/main/default/classes/`, `src/classes/`, `classes/`
@@ -161,6 +164,7 @@ lwc-convert aura MyComponent --dry-run --verbose
161
164
  ```bash
162
165
  lwc-convert aura <name-or-path> # Convert Aura component bundle
163
166
  lwc-convert vf <name-or-path> # Convert Visualforce page
167
+ lwc-convert grade [target] # Assess conversion complexity
164
168
  ```
165
169
 
166
170
  ### Global Options
@@ -199,6 +203,21 @@ lwc-convert vf <page-path> [options]
199
203
  | `--dry-run` | Preview without writing files | `false` |
200
204
  | `--verbose` | Show detailed logs | `false` |
201
205
 
206
+ ### Grade Command Options
207
+
208
+ ```bash
209
+ lwc-convert grade [target] [options]
210
+ ```
211
+
212
+ | Option | Description | Default |
213
+ |--------|-------------|---------|
214
+ | `-t, --type <type>` | Component type (`aura`, `vf`, `both`) | `both` |
215
+ | `-o, --output <file>` | Output file for report | — |
216
+ | `--format <format>` | Output format (`json`, `console`, `md`) | `console` |
217
+ | `--detailed` | Show detailed breakdown | `false` |
218
+ | `--sort-by <field>` | Sort by `score`, `complexity`, or `name` | `score` |
219
+ | `--filter <filter>` | Filter results (e.g., `grade:D,F`) | — |
220
+
202
221
  ---
203
222
 
204
223
  ## 🔄 Output Modes
@@ -595,17 +614,20 @@ npm test -- --coverage # Generate coverage report
595
614
  ## ⚠️ Limitations
596
615
 
597
616
  ### General
617
+
598
618
  - **Single component only** — No batch processing by design
599
619
  - **Static analysis** — Cannot detect runtime behavior
600
620
  - **Manual testing required** — Generated code should be tested
601
621
 
602
622
  ### Aura-Specific
623
+
603
624
  - Complex/nested expressions may need manual adjustment
604
625
  - `$A.createComponent` patterns require manual migration
605
626
  - Application events need manual pub/sub or LMS setup
606
627
  - Custom renderers need manual conversion
607
628
 
608
629
  ### Visualforce-Specific
630
+
609
631
  - `apex:actionRegion` needs architectural redesign
610
632
  - Page includes need component composition
611
633
  - `renderAs="pdf"` has no LWC equivalent
@@ -689,7 +711,11 @@ Contributions are welcome! Here's how to help:
689
711
  - Follow existing code style
690
712
  - Add tests for new features
691
713
  - Update documentation for new mappings
692
- - Keep commits focused and atomic
714
+ - Use [Conventional Commits](https://www.conventionalcommits.org/) format:
715
+ - `feat: add new feature` (triggers minor release)
716
+ - `fix: resolve bug` (triggers patch release)
717
+ - `docs: update README` (no release)
718
+ - `feat!: breaking change` (triggers major release)
693
719
 
694
720
  ---
695
721
 
@@ -0,0 +1,2 @@
1
+ export declare function grade(target: string | undefined, options: any): Promise<void>;
2
+ //# sourceMappingURL=grade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grade.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/grade.ts"],"names":[],"mappings":"AAOA,wBAAsB,KAAK,CACvB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,GAAG,GACb,OAAO,CAAC,IAAI,CAAC,CAuGf"}
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.grade = grade;
37
+ const fs = __importStar(require("fs-extra"));
38
+ const logger_1 = require("../../utils/logger");
39
+ const grader_1 = require("../../grading/grader");
40
+ const path_resolver_1 = require("../../utils/path-resolver");
41
+ async function grade(target, options) {
42
+ const grader = new grader_1.Grader();
43
+ // Determine scope and target
44
+ let scope = 'project';
45
+ let targetPath = target;
46
+ if (target) {
47
+ if (await fs.pathExists(target)) {
48
+ const stat = await fs.stat(target);
49
+ if (stat.isDirectory()) {
50
+ // Check if it's a component bundle or just a folder
51
+ const files = await fs.readdir(target);
52
+ if (files.some(f => f.endsWith('.cmp'))) {
53
+ scope = 'component';
54
+ }
55
+ else {
56
+ scope = 'folder';
57
+ }
58
+ }
59
+ else {
60
+ scope = 'file';
61
+ }
62
+ }
63
+ else {
64
+ // Try to resolve as component name
65
+ if (options.type === 'aura') {
66
+ const resolved = await (0, path_resolver_1.resolveAuraPath)(target);
67
+ if (resolved.found) {
68
+ targetPath = resolved.path;
69
+ scope = 'component';
70
+ }
71
+ }
72
+ else if (options.type === 'vf') {
73
+ const resolved = await (0, path_resolver_1.resolveVfPath)(target);
74
+ if (resolved.found) {
75
+ targetPath = resolved.path;
76
+ scope = 'file'; // VF pages are single files
77
+ }
78
+ }
79
+ }
80
+ }
81
+ const gradingOptions = {
82
+ type: options.type || 'both',
83
+ scope,
84
+ targetPath,
85
+ detailLevel: options.detailed ? 'detailed' : 'summary',
86
+ sortBy: options.sortBy,
87
+ filter: options.filter,
88
+ exportFormats: options.format ? [options.format] : ['console'],
89
+ exportDir: options.output,
90
+ dryRun: options.dryRun
91
+ };
92
+ logger_1.logger.banner();
93
+ logger_1.logger.header('Conversion Complexity Grading');
94
+ logger_1.logger.info(`Type: ${gradingOptions.type}`);
95
+ logger_1.logger.info(`Scope: ${gradingOptions.scope}`);
96
+ if (targetPath)
97
+ logger_1.logger.info(`Target: ${targetPath}`);
98
+ logger_1.logger.divider();
99
+ try {
100
+ logger_1.logger.info('Grading components...');
101
+ const results = await grader.grade(gradingOptions);
102
+ if (results.length === 0) {
103
+ logger_1.logger.warn('No components found to grade.');
104
+ return;
105
+ }
106
+ // Sort results
107
+ if (options.sortBy) {
108
+ sortResults(results, options.sortBy);
109
+ }
110
+ else {
111
+ // Default sort by score ascending (hardest first? or easiest? Plan says score-high default in TUI)
112
+ // Let's sort by score ascending (lowest score/hardest first) to highlight issues?
113
+ // Or descending (best first)?
114
+ // Plan says "Sort by score (highest first)" in TUI example.
115
+ results.sort((a, b) => b.overallScore - a.overallScore);
116
+ }
117
+ // Filter results
118
+ const filteredResults = filterResults(results, options.filter);
119
+ // Generate summary
120
+ const summary = grader.generateSummary(filteredResults);
121
+ // Output results
122
+ if (options.format === 'json') {
123
+ const output = { summary, components: filteredResults };
124
+ if (options.output) {
125
+ await fs.writeJson(options.output, output, { spaces: 2 });
126
+ logger_1.logger.success(`Results written to ${options.output}`);
127
+ }
128
+ else {
129
+ console.log(JSON.stringify(output, null, 2));
130
+ }
131
+ }
132
+ else {
133
+ // Console output
134
+ printConsoleReport(filteredResults, summary, options.detailed);
135
+ }
136
+ }
137
+ catch (error) {
138
+ logger_1.logger.error(`Grading failed: ${error.message}`);
139
+ if (options.verbose)
140
+ console.error(error);
141
+ process.exit(1);
142
+ }
143
+ }
144
+ function sortResults(results, sortBy) {
145
+ switch (sortBy) {
146
+ case 'score':
147
+ results.sort((a, b) => b.overallScore - a.overallScore);
148
+ break;
149
+ case 'complexity':
150
+ // Map complexity to number
151
+ const complexityMap = {
152
+ 'Simple': 1, 'Easy': 2, 'Moderate': 3, 'Complex': 4, 'Very Complex': 5
153
+ };
154
+ results.sort((a, b) => complexityMap[a.complexity] - complexityMap[b.complexity]);
155
+ break;
156
+ case 'name':
157
+ results.sort((a, b) => a.componentName.localeCompare(b.componentName));
158
+ break;
159
+ }
160
+ }
161
+ function filterResults(results, filter) {
162
+ if (!filter)
163
+ return results;
164
+ // Simple filter implementation: "grade:D,F" or "score:<60"
165
+ if (filter.startsWith('grade:')) {
166
+ const grades = filter.substring(6).split(',');
167
+ return results.filter(r => grades.includes(r.letterGrade));
168
+ }
169
+ if (filter.startsWith('score:')) {
170
+ const condition = filter.substring(6);
171
+ if (condition.startsWith('<')) {
172
+ const val = parseInt(condition.substring(1));
173
+ return results.filter(r => r.overallScore < val);
174
+ }
175
+ if (condition.startsWith('>')) {
176
+ const val = parseInt(condition.substring(1));
177
+ return results.filter(r => r.overallScore > val);
178
+ }
179
+ }
180
+ return results;
181
+ }
182
+ function printConsoleReport(results, summary, detailed) {
183
+ // Summary Table
184
+ console.log('Component'.padEnd(30) + 'Type'.padEnd(10) + 'Score'.padEnd(10) + 'Grade'.padEnd(10) + 'Complexity');
185
+ console.log('-'.repeat(80));
186
+ results.forEach(r => {
187
+ console.log(r.componentName.padEnd(30) +
188
+ r.componentType.padEnd(10) +
189
+ r.overallScore.toString().padEnd(10) +
190
+ r.letterGrade.padEnd(10) +
191
+ r.complexity);
192
+ });
193
+ logger_1.logger.blank();
194
+ logger_1.logger.summaryBox('Grading Summary', [
195
+ { label: 'Total Components', value: summary.totalComponents.toString(), type: 'info' },
196
+ { label: 'Average Score', value: `${summary.averageScore} (${summary.averageGrade})`, type: 'info' },
197
+ { label: 'Manual Effort', value: `${summary.totalEffort.manualHours.estimate} hours`, type: 'warn' }
198
+ ]);
199
+ if (detailed) {
200
+ logger_1.logger.divider();
201
+ logger_1.logger.subheader('Detailed Breakdown');
202
+ results.forEach(r => {
203
+ logger_1.logger.info(`${r.componentName} (${r.letterGrade})`);
204
+ r.complexityFactors.forEach(f => {
205
+ console.log(` - [${f.category}] ${f.factor}`);
206
+ });
207
+ logger_1.logger.blank();
208
+ });
209
+ }
210
+ }
211
+ //# sourceMappingURL=grade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grade.js","sourceRoot":"","sources":["../../../src/cli/commands/grade.ts"],"names":[],"mappings":"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"}
@@ -3,16 +3,19 @@
3
3
  * Redesigned with @clack/prompts for a modern, polished experience
4
4
  */
5
5
  import { VfControllerReference } from '../utils/vf-controller-resolver';
6
+ import { GradingOptions } from '../grading/types';
6
7
  export interface TuiAnswers {
7
- conversionType: 'aura' | 'vf';
8
- componentPath: string;
8
+ action: 'convert' | 'grade';
9
+ conversionType?: 'aura' | 'vf';
10
+ componentPath?: string;
9
11
  controllerPath?: string;
10
12
  controllerPaths?: string[];
11
13
  detectedControllers?: VfControllerReference[];
12
- outputDir: string;
13
- conversionMode: 'scaffolding' | 'full';
14
- openFolder: boolean;
15
- preview: boolean;
14
+ outputDir?: string;
15
+ conversionMode?: 'scaffolding' | 'full';
16
+ openFolder?: boolean;
17
+ preview?: boolean;
18
+ gradingOptions?: GradingOptions;
16
19
  }
17
20
  /**
18
21
  * Run the interactive TUI
@@ -1 +1 @@
1
- {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/cli/interactive.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAEL,qBAAqB,EAEtB,MAAM,iCAAiC,CAAC;AAEzC,MAAM,WAAW,UAAU;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,aAAa,GAAG,MAAM,CAAC;IACvC,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB;AAoLD;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAgapE"}
1
+ {"version":3,"file":"interactive.d.ts","sourceRoot":"","sources":["../../src/cli/interactive.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAEL,qBAAqB,EAEtB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAoLD;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAugBpE"}
@@ -215,6 +215,7 @@ async function runInteractiveTui() {
215
215
  showHeader();
216
216
  // State management
217
217
  let currentStep = 0;
218
+ let action = 'convert';
218
219
  let conversionType;
219
220
  let componentPath;
220
221
  let controllerPath;
@@ -224,22 +225,57 @@ async function runInteractiveTui() {
224
225
  let conversionMode = 'scaffolding';
225
226
  let openFolder = true;
226
227
  let preview = false;
228
+ let gradingOptions;
227
229
  // Main wizard loop - allows navigation back to any step
228
230
  wizardLoop: while (currentStep < 4) {
229
- // Step 1: Select conversion type
231
+ // Step 1: Select action and type
230
232
  while (currentStep === 0) {
231
233
  showBreadcrumbs(currentStep);
232
- const typeResult = await p.select({
233
- message: 'What would you like to convert?',
234
+ const actionResult = await p.select({
235
+ message: 'What would you like to do?',
234
236
  options: [
235
- { value: 'aura', label: '⚡ Aura Component → LWC', hint: 'Convert Aura bundles' },
236
- { value: 'vf', label: '📄 Visualforce Page → LWC', hint: 'Convert VF pages' },
237
+ { value: 'convert_aura', label: '⚡ Convert Aura Component', hint: 'Convert Aura bundles to LWC' },
238
+ { value: 'convert_vf', label: '📄 Convert Visualforce Page', hint: 'Convert VF pages to LWC' },
239
+ { value: 'grade', label: '📊 Grade Complexity', hint: 'Analyze components before conversion' },
237
240
  ],
238
241
  });
239
- if (isCancel(typeResult))
242
+ if (isCancel(actionResult))
240
243
  return handleCancel();
241
- conversionType = typeResult;
242
- currentStep = 1;
244
+ const selected = actionResult;
245
+ if (selected === 'grade') {
246
+ action = 'grade';
247
+ // Grading flow
248
+ const gradeTypeResult = await p.select({
249
+ message: 'What would you like to grade?',
250
+ options: [
251
+ { value: 'aura', label: '⚡ Aura Components' },
252
+ { value: 'vf', label: '📄 Visualforce Pages' },
253
+ { value: 'both', label: '🔄 Both (Project Scan)' },
254
+ ]
255
+ });
256
+ if (isCancel(gradeTypeResult))
257
+ return handleCancel();
258
+ // Setup default grading options
259
+ gradingOptions = {
260
+ type: gradeTypeResult,
261
+ scope: 'project', // Default
262
+ detailLevel: 'summary',
263
+ exportFormats: ['console']
264
+ };
265
+ // If both, we default to project scope and skip to options
266
+ if (gradeTypeResult === 'both') {
267
+ currentStep = 3; // Jump to options
268
+ }
269
+ else {
270
+ conversionType = gradeTypeResult;
271
+ currentStep = 1; // Go to scope selection (reuse component selection logic)
272
+ }
273
+ }
274
+ else {
275
+ action = 'convert';
276
+ conversionType = selected === 'convert_aura' ? 'aura' : 'vf';
277
+ currentStep = 1;
278
+ }
243
279
  }
244
280
  // Step 2: Select source component/page
245
281
  while (currentStep === 1) {
@@ -386,9 +422,17 @@ async function runInteractiveTui() {
386
422
  }
387
423
  currentStep = 2;
388
424
  }
425
+ // If grading, update target path and skip controller selection
426
+ if (action === 'grade' && componentPath) {
427
+ if (gradingOptions) {
428
+ gradingOptions.targetPath = componentPath;
429
+ gradingOptions.scope = conversionType === 'aura' ? 'component' : 'file';
430
+ }
431
+ currentStep = 3; // Skip to options
432
+ }
389
433
  }
390
- // Step 3: Controllers (VF only)
391
- while (currentStep === 2 && conversionType === 'vf' && componentPath) {
434
+ // Step 3: Controllers (VF only, skip for grading)
435
+ while (currentStep === 2 && conversionType === 'vf' && componentPath && action !== 'grade') {
392
436
  showBreadcrumbs(currentStep, conversionType);
393
437
  const resolvedPagePath = path.resolve(componentPath);
394
438
  if (await fs.pathExists(resolvedPagePath)) {
@@ -494,6 +538,44 @@ async function runInteractiveTui() {
494
538
  // Step 4: Options
495
539
  while (currentStep === 3) {
496
540
  showBreadcrumbs(currentStep, conversionType);
541
+ if (action === 'grade' && gradingOptions) {
542
+ // Grading Options
543
+ const detailResult = await p.select({
544
+ message: 'Detail level:',
545
+ options: [
546
+ { value: 'summary', label: '📊 Summary', hint: 'Overview with scores' },
547
+ { value: 'standard', label: '📋 Standard', hint: 'Category breakdowns' },
548
+ { value: 'detailed', label: '🔍 Detailed', hint: 'Full analysis with factors' },
549
+ ]
550
+ });
551
+ if (isCancel(detailResult))
552
+ return handleCancel();
553
+ gradingOptions.detailLevel = detailResult;
554
+ const exportResult = await p.multiselect({
555
+ message: 'Export options:',
556
+ options: [
557
+ { value: 'console', label: '🖥️ Console display', hint: 'Show in terminal' },
558
+ { value: 'json', label: '💾 JSON export', hint: 'Save to file' },
559
+ ],
560
+ initialValues: ['console'],
561
+ required: true
562
+ });
563
+ if (isCancel(exportResult))
564
+ return handleCancel();
565
+ gradingOptions.exportFormats = exportResult;
566
+ if (exportResult.includes('json')) {
567
+ const outputResult = await p.text({
568
+ message: 'Export file path:',
569
+ initialValue: './grading-report.json',
570
+ });
571
+ if (isCancel(outputResult))
572
+ return handleCancel();
573
+ gradingOptions.exportDir = outputResult;
574
+ }
575
+ currentStep = 4;
576
+ continue wizardLoop;
577
+ }
578
+ // Conversion Options
497
579
  const modeResult = await p.select({
498
580
  message: 'Conversion mode:',
499
581
  options: [
@@ -548,31 +630,45 @@ async function runInteractiveTui() {
548
630
  } // end wizardLoop
549
631
  // Step 5: Confirmation
550
632
  showBreadcrumbs(currentStep, conversionType);
551
- const summaryLines = [
552
- `${picocolors_1.default.dim('Type:')} ${conversionType === 'aura' ? '⚡ Aura → LWC' : '📄 VF → LWC'}`,
553
- `${picocolors_1.default.dim('Source:')} ${componentPath}`,
554
- ];
555
- if (controllerPaths.length > 0) {
556
- summaryLines.push(`${picocolors_1.default.dim('Controllers:')} ${controllerPaths.length} selected`);
557
- controllerPaths.forEach(cp => {
558
- summaryLines.push(` ${picocolors_1.default.dim('•')} ${path.basename(cp)}`);
559
- });
633
+ const summaryLines = [];
634
+ if (action === 'grade') {
635
+ summaryLines.push(`${picocolors_1.default.dim('Action:')} 📊 Grade Complexity`);
636
+ summaryLines.push(`${picocolors_1.default.dim('Type:')} ${gradingOptions?.type === 'both' ? 'Project Scan' : (gradingOptions?.type === 'aura' ? '⚡ Aura' : '📄 VF')}`);
637
+ if (gradingOptions?.targetPath) {
638
+ summaryLines.push(`${picocolors_1.default.dim('Target:')} ${gradingOptions.targetPath}`);
639
+ }
640
+ else {
641
+ summaryLines.push(`${picocolors_1.default.dim('Scope:')} Entire Project`);
642
+ }
643
+ summaryLines.push(`${picocolors_1.default.dim('Detail:')} ${gradingOptions?.detailLevel}`);
644
+ summaryLines.push(`${picocolors_1.default.dim('Export:')} ${gradingOptions?.exportFormats?.join(', ')}`);
645
+ }
646
+ else {
647
+ summaryLines.push(`${picocolors_1.default.dim('Type:')} ${conversionType === 'aura' ? '⚡ Aura → LWC' : '📄 VF → LWC'}`);
648
+ summaryLines.push(`${picocolors_1.default.dim('Source:')} ${componentPath}`);
649
+ if (controllerPaths.length > 0) {
650
+ summaryLines.push(`${picocolors_1.default.dim('Controllers:')} ${controllerPaths.length} selected`);
651
+ controllerPaths.forEach(cp => {
652
+ summaryLines.push(` ${picocolors_1.default.dim('•')} ${path.basename(cp)}`);
653
+ });
654
+ }
655
+ summaryLines.push(`${picocolors_1.default.dim('Mode:')} ${conversionMode === 'full' ? '⚡ Full' : '📝 Scaffolding'}`);
656
+ summaryLines.push(`${picocolors_1.default.dim('Output:')} ${outputDir}`);
657
+ summaryLines.push(`${picocolors_1.default.dim('Open folder:')} ${openFolder ? '✓ Yes' : '✗ No'}`);
658
+ summaryLines.push(`${picocolors_1.default.dim('UI Preview:')} ${preview ? '✓ Yes' : '✗ No'}`);
560
659
  }
561
- summaryLines.push(`${picocolors_1.default.dim('Mode:')} ${conversionMode === 'full' ? ' Full' : '📝 Scaffolding'}`);
562
- summaryLines.push(`${picocolors_1.default.dim('Output:')} ${outputDir}`);
563
- summaryLines.push(`${picocolors_1.default.dim('Open folder:')} ${openFolder ? '✓ Yes' : '✗ No'}`);
564
- summaryLines.push(`${picocolors_1.default.dim('UI Preview:')} ${preview ? '✓ Yes' : '✗ No'}`);
565
- p.note(summaryLines.join('\n'), '📋 Conversion Summary');
660
+ p.note(summaryLines.join('\n'), action === 'grade' ? '📋 Grading Configuration' : '📋 Conversion Summary');
566
661
  const confirm = await p.confirm({
567
- message: 'Proceed with conversion?',
662
+ message: action === 'grade' ? 'Proceed with grading?' : 'Proceed with conversion?',
568
663
  initialValue: true,
569
664
  });
570
665
  if (isCancel(confirm) || !confirm) {
571
- p.cancel('Conversion cancelled.');
666
+ p.cancel('Operation cancelled.');
572
667
  return null;
573
668
  }
574
- p.outro(picocolors_1.default.green('Starting conversion...'));
669
+ p.outro(picocolors_1.default.green(action === 'grade' ? 'Starting grading...' : 'Starting conversion...'));
575
670
  return {
671
+ action,
576
672
  conversionType: conversionType,
577
673
  componentPath: componentPath,
578
674
  controllerPath,
@@ -582,6 +678,7 @@ async function runInteractiveTui() {
582
678
  conversionMode,
583
679
  openFolder,
584
680
  preview,
681
+ gradingOptions
585
682
  };
586
683
  }
587
684
  //# sourceMappingURL=interactive.js.map