lwc-generator-cli
Version:
AI-powered Lightning Web Component generator with interactive web UI
203 lines (166 loc) • 5.04 kB
JavaScript
const express = require('express');
const path = require('path');
const fs = require('fs').promises;
const open = require('open');
const archiver = require('archiver');
async function startServer(options) {
const { port, files, componentName, outputDir } = options;
const app = express();
app.use(express.json());
app.use(express.static(path.join(__dirname, '../public')));
// Serve Monaco Editor from CDN in HTML
// API endpoint to get file tree
app.get('/api/files', (req, res) => {
const tree = buildFileTree(files);
res.json({
componentName,
tree,
files: files.map(f => ({
name: f.name,
type: f.type,
size: f.size,
relativePath: f.relativePath
}))
});
});
// API endpoint to get file content
app.get('/api/file/:path(*)', async (req, res) => {
try {
const filePath = decodeURIComponent(req.params.path);
const file = files.find(f => f.relativePath === filePath);
if (!file) {
return res.status(404).json({ error: 'File not found' });
}
res.json({
name: file.name,
type: file.type,
content: file.content,
size: file.size
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// API endpoint to update file content
app.post('/api/file/:path(*)', async (req, res) => {
try {
const filePath = decodeURIComponent(req.params.path);
const file = files.find(f => f.relativePath === filePath);
if (!file) {
return res.status(404).json({ error: 'File not found' });
}
file.content = req.body.content;
await fs.writeFile(file.path, req.body.content, 'utf-8');
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// API endpoint to download single file
app.get('/api/download/:path(*)', async (req, res) => {
try {
const filePath = decodeURIComponent(req.params.path);
const file = files.find(f => f.relativePath === filePath);
if (!file) {
return res.status(404).json({ error: 'File not found' });
}
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', `attachment; filename="${file.name}"`);
res.send(file.content);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// API endpoint to download all files as ZIP
app.get('/api/download-zip', (req, res) => {
res.setHeader('Content-Type', 'application/zip');
res.setHeader('Content-Disposition', `attachment; filename="${componentName}.zip"`);
const archive = archiver('zip', { zlib: { level: 9 } });
archive.on('error', (err) => {
res.status(500).json({ error: err.message });
});
archive.pipe(res);
// Add files to archive
files.forEach(file => {
archive.append(file.content, { name: `${componentName}/${file.relativePath}` });
});
archive.finalize();
});
const server = app.listen(port, () => {
// Open browser
open(`http://localhost:${port}`);
});
return server;
}
function buildFileTree(files) {
const tree = {
name: 'force-app',
type: 'folder',
children: []
};
// Create the nested structure
const mainFolder = {
name: 'main',
type: 'folder',
children: []
};
const defaultFolder = {
name: 'default',
type: 'folder',
children: []
};
// Separate LWC files and Apex files
const lwcFiles = files.filter(f => f.relativePath.includes('/lwc/'));
const apexFiles = files.filter(f => f.relativePath.includes('/classes/'));
// Add LWC folder if there are LWC files
if (lwcFiles.length > 0) {
const lwcFolder = {
name: 'lwc',
type: 'folder',
children: []
};
// Group LWC files by component name
const componentName = lwcFiles[0].relativePath.split('/lwc/')[1].split('/')[0];
const componentFolder = {
name: componentName,
type: 'folder',
children: []
};
lwcFiles.forEach(file => {
const fileName = file.name;
componentFolder.children.push({
name: fileName,
type: 'file',
fileType: file.type,
size: file.size,
path: file.relativePath
});
});
lwcFolder.children.push(componentFolder);
defaultFolder.children.push(lwcFolder);
}
// Add classes folder if there are apex files
if (apexFiles.length > 0) {
const classesFolder = {
name: 'classes',
type: 'folder',
children: []
};
apexFiles.forEach(file => {
classesFolder.children.push({
name: file.name,
type: 'file',
fileType: file.type,
size: file.size,
path: file.relativePath
});
});
defaultFolder.children.push(classesFolder);
}
mainFolder.children.push(defaultFolder);
tree.children.push(mainFolder);
return tree;
}
module.exports = {
startServer
};