lwc-convert
v1.8.0
Published
CLI tool to convert Salesforce Aura and Visualforce components to Lightning Web Components
Maintainers
Readme
🔄 lwc-convert
A precision TypeScript CLI tool for converting Salesforce Aura components and Visualforce pages to Lightning Web Components (LWC)
lwc-convert automates the migration of legacy Salesforce UI technologies to modern Lightning Web Components:
| Source Technology | Target | Confidence | |-------------------|--------|------------| | Aura Components | LWC | High | | Visualforce Pages | LWC | Medium-High |
What You Get
For each conversion, the tool generates a complete LWC bundle:
myComponent/
├── myComponent.html # Converted template
├── myComponent.js # ES6 class with decorators
├── myComponent.css # Scoped styles
├── myComponent.js-meta.xml # Component configuration
└── CONVERSION_NOTES.md # Action items & guidance✨ Features
🎯 Single-Component Focus
Unlike batch tools that sacrifice accuracy, lwc-convert processes one component at a time for maximum precision and detailed error reporting.
📝 Two Output Modes
| Mode | Description | Best For |
|------|-------------|----------|
| Scaffolding (default) | Generates skeleton with // TODO: comments | Complex components requiring manual attention |
| Full Conversion (--full) | Complete transformation with // REVIEW: markers | Simple, standard components |
🔍 Smart Analysis
- Parses Aura markup, controllers, helpers, and styles
- Analyzes Apex controllers for VF pages
- Detects patterns and suggests modern equivalents
- Identifies potential issues upfront
- New! 📊 Complexity Grading: Analyze components before conversion to estimate effort and identify risks
- New! 📈 CSV Export: Export grading reports for team tracking and stakeholder updates
- New! 🔔 Update Notifications: Get notified when new versions are available
📋 Conversion Notes
Every conversion includes a detailed markdown file with:
- ✅ Completed transformations
- ⚠️ Items needing manual attention
- 📖 Migration guidance and best practices
🚀 Quick Start
Interactive Mode
Just run lwc-convert without arguments to launch the interactive TUI:
npx lwc-convertYou'll be guided through:
- Select conversion type (Aura or Visualforce)
- Choose component from auto-discovered list or enter path
- Configure options (scaffolding/full, output dir, open folder)
- Confirm and convert!
Prerequisites
- Node.js 18.0 or higher
- npm 8.0 or higher
Installation
Via npm (recommended):
# Run directly with npx (no install needed)
npx lwc-convert aura AccountCard
# Or install globally
npm install -g lwc-convert
lwc-convert aura AccountCardFrom source (for development):
git clone https://github.com/Lastonedown86/lwc-convert.git
cd lwc-convert
npm install
npm run build
npm linkYour First Conversion
Convert an Aura Component:
# Just use the component name — the CLI searches automatically!
lwc-convert aura AccountCard
# Or use a full path
lwc-convert aura ./force-app/main/default/aura/AccountCard
# Full conversion mode
lwc-convert aura AccountCard --fullConvert a Visualforce Page:
# Just use the page name (with or without .page extension)
lwc-convert vf ContactList
# With Apex controller (also supports just the class name)
lwc-convert vf ContactList --controller ContactListController
# Or use full paths
lwc-convert vf ./pages/ContactList.page --controller ./classes/ContactListController.clsPreview Without Writing Files:
lwc-convert aura MyComponent --dry-run --verboseAssess Conversion Complexity:
# Grade a single component
lwc-convert grade AccountCard --type aura
# Scan entire project and export report
lwc-convert grade --type both --format json -o report.json
# Export to CSV for team tracking (Excel/Sheets compatible)
lwc-convert grade --format csv -o migration-status.csv
# Filter to only problematic components
lwc-convert grade --filter "grade:D,F" --format csv -o needs-attention.csv💡 Smart Path Resolution: The CLI automatically searches common Salesforce project locations:
force-app/main/default/aura/,src/aura/,aura/force-app/main/default/pages/,src/pages/,pages/force-app/main/default/classes/,src/classes/,classes/
📖 CLI Reference
Commands
lwc-convert # Launch interactive TUI
lwc-convert aura <name-or-path> # Convert Aura component bundle
lwc-convert vf <name-or-path> # Convert Visualforce page
lwc-convert grade [target] # Assess conversion complexity
lwc-convert deps [target] # Analyze component dependencies
lwc-convert session # View session info and patternsGlobal Options
| Option | Description |
|--------|-------------|
| -V, --version | Display version |
| -h, --help | Show help |
Aura Command Options
lwc-convert aura <bundle-path> [options]| Option | Description | Default |
|--------|-------------|---------|
| --full | Full conversion instead of scaffolding | false |
| -o, --output <dir> | Output directory | ./lwc-output |
| --open | Open output folder in file explorer | false |
| --dry-run | Preview without writing files | false |
| --verbose | Show detailed logs | false |
VF Command Options
lwc-convert vf <page-path> [options]| Option | Description | Default |
|--------|-------------|---------|
| --full | Full conversion instead of scaffolding | false |
| -o, --output <dir> | Output directory | ./lwc-output |
| --controller <path> | Apex controller for enhanced analysis | — |
| --open | Open output folder in file explorer | false |
| --dry-run | Preview without writing files | false |
| --verbose | Show detailed logs | false |
Grade Command Options
lwc-convert grade [target] [options]| Option | Description | Default |
|--------|-------------|---------|
| -t, --type <type> | Component type (aura, vf, both) | both |
| -o, --output <file> | Output file for report | — |
| --format <format> | Output format (json, csv, console) | console |
| --detailed | Show detailed breakdown | false |
| --sort-by <field> | Sort by score, complexity, or name | score |
| --filter <filter> | Filter results (e.g., grade:D,F) | — |
Deps Command Options
lwc-convert deps [target] [options]| Option | Description | Default |
|--------|-------------|---------|
| -t, --type <type> | Component type (aura, vf, both) | both |
| -o, --output <file> | Output file path | — |
| --format <format> | Output format (console, json, mermaid) | console |
| --conversion-order | Show recommended conversion order | false |
| --focus <component> | Focus on specific component | — |
| --depth <n> | Maximum depth to traverse (0 = unlimited) | 0 |
| --circular-only | Only show circular dependencies | false |
Session Command Options
lwc-convert session [options]| Option | Description |
|--------|-------------|
| --report | Generate full session report |
| --patterns | Show learned patterns from session |
| --cleanup | Clean up session data |
🔄 Output Modes
Scaffolding Mode (Default)
Generates an LWC skeleton optimized for manual completion:
import { LightningElement, api } from 'lwc';
// TODO: Import Apex method - verify class and method name
// import getRecords from '@salesforce/apex/MyController.getRecords';
export default class MyComponent extends LightningElement {
// TODO: Verify if this should be @api (public) or private
@api recordId;
// Converted from aura:attribute "items"
items = [];
// TODO: Migrate logic from Aura init handler
connectedCallback() {
// Original init logic goes here
}
// TODO: Implement - converted from controller.handleSave
handleSave(event) {
// Original: component.get("v.record"), then server call
}
}Full Conversion Mode (--full)
Attempts complete code transformation:
import { LightningElement, api, wire } from 'lwc';
import getRecords from '@salesforce/apex/MyController.getRecords';
export default class MyComponent extends LightningElement {
@api recordId;
items = [];
isLoading = false;
connectedCallback() {
this.loadRecords();
}
async loadRecords() {
this.isLoading = true;
try {
// REVIEW: Verify Apex method parameters
this.items = await getRecords({ recordId: this.recordId });
} catch (error) {
console.error('Error loading records:', error);
} finally {
this.isLoading = false;
}
}
handleSave(event) {
// REVIEW: Complex logic converted - verify behavior
const record = { ...this.currentRecord };
// ... conversion continues
}
}📊 Migration Tracking
CSV Export for Teams
Export grading results to CSV for tracking migration progress in spreadsheets:
# Full project export
lwc-convert grade --format csv -o migration-status.csv
# Only components needing attention
lwc-convert grade --filter "grade:D,F" --format csv -o needs-attention.csvCSV includes:
- Component name, type, and file path
- Score (0-100) and letter grade (A-F)
- Complexity level and estimated manual hours
- Automated conversion percentage
- Warning count per component
- Summary section with totals and grade distribution
Dependency Analysis
Visualize component relationships and plan conversion order:
# Show all dependencies
lwc-convert deps
# Get recommended conversion order (leaves first)
lwc-convert deps --conversion-order
# Focus on one component's dependency tree
lwc-convert deps --focus AccountCard
# Export as Mermaid diagram for documentation
lwc-convert deps --format mermaid -o deps.mdSession Learning
The tool learns patterns during your session to improve suggestions:
# View session summary
lwc-convert session
# See learned conversion patterns
lwc-convert session --patterns
# Full session report
lwc-convert session --report🔔 Update Notifications
Starting with v1.5.0, lwc-convert automatically checks for updates and notifies you when a new version is available:
╭───────────────────────────────────────────────╮
│ Update available: 1.5.0 → 1.6.0 │
│ Run npm i -g lwc-convert to update │
╰───────────────────────────────────────────────╯- Non-blocking: Check runs in background, doesn't slow commands
- Cached: Only checks npm registry once per 24 hours
- Graceful: Silently skips if offline or npm is unreachable
📋 Conversion Mappings
Aura to LWC
| Aura | LWC |
|------|-----|
| <aura:component> | <template> |
| <aura:if isTrue="{!v.show}"> | <template if:true={show}> |
| <aura:if isFalse="{!v.hide}"> | <template if:false={hide}> |
| <aura:iteration items="{!v.list}" var="item"> | <template for:each={list} for:item="item"> |
| <aura:set attribute="x"> | Named slots or JavaScript |
| <aura:html tag="div"> | Direct HTML element |
| Aura | LWC |
|------|-----|
| {!v.propertyName} | {propertyName} |
| {!v.object.field} | {object.field} |
| {!c.handleClick} | {handleClick} |
| {!globalId} | data-id attribute |
| {!$Label.ns.name} | Import from @salesforce/label |
| Aura | LWC |
|------|-----|
| <aura:attribute name="x" type="String"/> | @api x; |
| <aura:attribute access="private"/> | Property without @api |
| <aura:attribute default="value"/> | x = 'value'; |
| aura:id="elementId" | data-id="elementId" |
| Aura | LWC |
|------|-----|
| component.get("v.name") | this.name |
| component.set("v.name", val) | this.name = val |
| component.find("auraId") | this.template.querySelector('[data-id="auraId"]') |
| helper.doSomething(cmp) | this.doSomething() |
| $A.enqueueAction(action) | Imperative Apex call |
| $A.getCallback(fn) | Direct function call |
| Aura Handler | LWC Lifecycle |
|--------------|---------------|
| init | connectedCallback() |
| render | renderedCallback() |
| afterRender | renderedCallback() |
| unrender | disconnectedCallback() |
| destroy | disconnectedCallback() |
| Aura | LWC |
|------|-----|
| $A.get("e.c:myEvent") | new CustomEvent('myevent', {...}) |
| event.fire() | this.dispatchEvent(event) |
| event.setParams({...}) | { detail: {...} } in CustomEvent |
| event.getParam("x") | event.detail.x |
| <aura:handler event="c:MyEvt" action="{!c.handle}"/> | onmyevent={handle} |
| <aura:registerEvent name="x"/> | Document in JSDoc |
| Aura | LWC |
|------|-----|
| <lightning:button> | <lightning-button> |
| <lightning:input> | <lightning-input> |
| <lightning:card> | <lightning-card> |
| <lightning:datatable> | <lightning-datatable> |
| <lightning:recordEditForm> | <lightning-record-edit-form> |
| iconName="standard:account" | icon-name="standard:account" |
| Aura | LWC |
|------|-----|
| $A.util.isEmpty(x) | !x \|\| x.length === 0 |
| $A.util.isUndefinedOrNull(x) | x === undefined \|\| x === null |
| $A.util.addClass(el, 'cls') | el.classList.add('cls') |
| $A.util.removeClass(el, 'cls') | el.classList.remove('cls') |
| $A.util.toggleClass(el, 'cls') | el.classList.toggle('cls') |
Visualforce to LWC
| Visualforce | LWC |
|-------------|-----|
| <apex:page> | <template> |
| <apex:form> | <lightning-record-edit-form> or <form> |
| <apex:pageBlock> | <lightning-card> |
| <apex:pageBlockSection> | <lightning-layout> |
| <apex:pageBlockButtons> | SLDS button group |
| Visualforce | LWC |
|-------------|-----|
| <apex:inputText> | <lightning-input type="text"> |
| <apex:inputTextarea> | <lightning-textarea> |
| <apex:inputCheckbox> | <lightning-input type="checkbox"> |
| <apex:inputField> | <lightning-input-field> |
| <apex:selectList> | <lightning-combobox> |
| <apex:selectRadio> | <lightning-radio-group> |
| Visualforce | LWC |
|-------------|-----|
| <apex:outputText> | {value} or <lightning-formatted-text> |
| <apex:outputField> | <lightning-output-field> |
| <apex:outputLabel> | <label> |
| <apex:outputLink> | <a> or NavigationMixin |
| Visualforce | LWC |
|-------------|-----|
| <apex:pageBlockTable> | <lightning-datatable> |
| <apex:dataTable> | <lightning-datatable> |
| <apex:repeat> | <template for:each> |
| <apex:dataList> | <template for:each> with <ul> |
| <apex:detail> | <lightning-record-form> |
| <apex:relatedList> | <lightning-related-list-view> |
| Visualforce | LWC |
|-------------|-----|
| <apex:commandButton> | <lightning-button onclick={handler}> |
| <apex:commandLink> | <lightning-button variant="base"> |
| <apex:actionFunction> | Imperative Apex call |
| <apex:actionSupport> | Event handler (onclick, onchange) |
| <apex:actionPoller> | setInterval in connectedCallback |
| <apex:actionStatus> | <lightning-spinner> with loading state |
| Visualforce | LWC |
|-------------|-----|
| <apex:pageMessages> | ShowToastEvent |
| <apex:messages> | ShowToastEvent |
| <apex:message> | <lightning-helptext> or validation |
| Visualforce | LWC |
|-------------|-----|
| {!$CurrentPage.parameters.id} | @wire(CurrentPageReference) |
| {!$User.Id} | import userId from '@salesforce/user/Id' |
| {!$Label.ns.name} | import label from '@salesforce/label/ns.name' |
| {!$Resource.name} | import res from '@salesforce/resourceUrl/name' |
| Visualforce | LWC |
|-------------|-----|
| Controller getter property | @wire decorator |
| Controller action method | Imperative Apex with @AuraEnabled |
| @RemoteAction | Imperative Apex with @AuraEnabled |
| rerender="sectionId" | Reactive property update |
| oncomplete="js()" | Promise .then() or async/await |
🏗️ Architecture
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Parsers │────▶│ Transformers │────▶│ Generators │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ • Aura Markup │ │ • Markup Trans. │ │ • Scaffolding │
│ • Aura JS │ │ • JS Transform │ │ • Full Convert │
│ • Aura CSS │ │ • Event Trans. │ │ • Meta XML │
│ • VF Page │ │ • Data Binding │ │ • Conv. Notes │
│ • Apex Class │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘Processing Pipeline
- Parse — Read and analyze source files (
.cmp,Controller.js,Helper.js,.css,.page,.cls) - Extract — Build intermediate representation (attributes, handlers, events, dependencies)
- Transform — Apply conversion rules (tag mappings, expressions, JavaScript patterns)
- Generate — Create complete LWC bundle (HTML, JS, CSS, meta XML)
- Document — Generate conversion notes with action items
📁 Project Structure
lwc-convert/
├── src/
│ ├── index.ts # CLI entry point
│ ├── cli/
│ │ ├── commands/
│ │ │ ├── aura.ts # Aura conversion command
│ │ │ └── vf.ts # VF conversion command
│ │ └── options.ts # CLI option definitions
│ ├── parsers/
│ │ ├── aura/ # Aura file parsers
│ │ └── vf/ # VF/Apex parsers
│ ├── transformers/
│ │ ├── aura-to-lwc/ # Aura transformation rules
│ │ └── vf-to-lwc/ # VF transformation rules
│ ├── generators/
│ │ ├── scaffolding.ts # Skeleton generator
│ │ └── full-conversion.ts # Complete converter
│ ├── mappings/ # Conversion mapping configs
│ └── utils/ # Shared utilities
├── tests/
│ ├── fixtures/ # Sample components for testing
│ └── __tests__/ # Test suites
├── package.json
├── tsconfig.json
└── README.md🧪 Development
Setup
npm install # Install dependencies
npm run build # Compile TypeScript
npm run dev -- aura ./path/to/component # Run with ts-nodeScripts
| Script | Description |
|--------|-------------|
| npm run build | Compile TypeScript → JavaScript |
| npm run dev | Run CLI with ts-node |
| npm test | Run Jest test suite |
| npm run lint | Run ESLint |
| npm run format | Format with Prettier |
Testing
npm test # Run all tests
npm test -- --testPathPattern=parsers # Run parser tests only
npm test -- --coverage # Generate coverage reportTech Stack
| Category | Technology | |----------|------------| | Language | TypeScript 5.x | | Runtime | Node.js 18+ | | CLI | Commander.js | | HTML/XML | htmlparser2 + domhandler | | JavaScript AST | Babel (parser, traverse, generator) | | CSS | PostCSS | | Testing | Jest + ts-jest | | Linting | ESLint | | Formatting | Prettier |
⚠️ Limitations
General
- Single component only — No batch processing by design
- Static analysis — Cannot detect runtime behavior
- Manual testing required — Generated code should be tested
Aura-Specific
- Complex/nested expressions may need manual adjustment
$A.createComponentpatterns require manual migration- Application events need manual pub/sub or LMS setup
- Custom renderers need manual conversion
Visualforce-Specific
apex:actionRegionneeds architectural redesign- Page includes need component composition
renderAs="pdf"has no LWC equivalent- Inline JavaScript needs manual migration
🔧 Troubleshooting
Ensure you're pointing to the component folder containing the .cmp file, not a parent directory.
# ✅ Correct
lwc-convert aura ./aura/MyComponent
# ❌ Incorrect
lwc-convert aura ./auraThe VF command requires a .page file path, not a directory.
# ✅ Correct
lwc-convert vf ./pages/MyPage.page
# ❌ Incorrect
lwc-convert vf ./pages/- Ensure Node.js 18+ is installed
- Delete
node_modulesand runnpm install - Check for TypeScript errors:
npm run build
Getting Help
- Run with
--verbosefor detailed output - Use
--dry-runto preview without writing files - Check the generated
CONVERSION_NOTES.mdfile - Review
// TODO:and// REVIEW:comments in generated code
🤝 Contributing
Contributions are welcome! Here's how to help:
Reporting Issues
- Check existing issues first
- Include reproduction steps
- Attach sample component (sanitized) if possible
- Include CLI output with
--verbose
Submitting Changes
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Write tests for new functionality
- Ensure all tests pass (
npm test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Guidelines
- Follow existing code style
- Add tests for new features
- Update documentation for new mappings
- Use Conventional Commits format:
feat: add new feature(triggers minor release)fix: resolve bug(triggers patch release)docs: update README(no release)feat!: breaking change(triggers major release)
📄 License
MIT License — Copyright © 2025
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
🙏 Acknowledgments
- Salesforce Lightning Web Components documentation
- Aura Components Developer Guide
- Visualforce Developer Guide
- The open source community for excellent parsing libraries
