lwc-linter
Version:
A comprehensive CLI tool for linting Lightning Web Components v8.0.0+ with modern LWC patterns, decorators, lifecycle hooks, and Salesforce platform integration
204 lines • 9.09 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DevServer = void 0;
const express_1 = __importDefault(require("express"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs-extra"));
const chalk_1 = __importDefault(require("chalk"));
const compression_1 = __importDefault(require("compression"));
const linter_1 = require("./linter");
const output_formatter_1 = require("./output-formatter");
class DevServer {
constructor(config, targetPath, port = 3000) {
this.currentResults = [];
this.app = (0, express_1.default)();
this.port = port;
this.config = config;
this.targetPath = targetPath;
this.reportPath = path.resolve('lwc-linter-live-report.html');
this.setupServer();
}
setupServer() {
// Enable gzip compression for faster loading
this.app.use((0, compression_1.default)({
level: 9, // Maximum compression
threshold: 1024, // Compress files larger than 1KB
filter: (req, res) => {
// Compress everything except already compressed formats
const contentType = res.getHeader('Content-Type');
if (typeof contentType === 'string') {
return !contentType.includes('image/') &&
!contentType.includes('video/') &&
!contentType.includes('audio/');
}
return true;
}
}));
// Enable CORS and proper headers for all routes
this.app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
res.header('Pragma', 'no-cache');
res.header('Expires', '0');
res.header('Vary', 'Accept-Encoding'); // Enable compression hint
next();
});
// Serve static files from current directory with optimized caching
this.app.use('/static', express_1.default.static('.', {
setHeaders: (res, path) => {
// Cache static assets for better performance
if (path.endsWith('.js') || path.endsWith('.css')) {
res.header('Cache-Control', 'public, max-age=3600'); // 1 hour cache
}
else {
res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
}
res.header('Vary', 'Accept-Encoding'); // Enable compression hint
}
}));
// API endpoint to get latest lint results (for manual refresh)
this.app.get('/api/lint-results', async (req, res) => {
try {
const linter = new linter_1.LWCLinter(this.config);
const results = await linter.lintDirectory(this.targetPath);
const response = {
timestamp: new Date().toISOString(),
results: results.map(result => ({
filePath: result.filePath,
issueCount: result.issues.length,
fixedCount: result.fixedCount || 0,
issues: result.issues
}))
};
this.currentResults = response.results;
res.header('Content-Type', 'application/json');
res.json(response);
}
catch (error) {
console.error('API Error:', error);
res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
}
});
// JavaScript error logging endpoint
this.app.post('/api/log-error', express_1.default.json(), (req, res) => {
const { message, line, column, error, stack, userAgent } = req.body;
console.error(chalk_1.default.red('🚨 BROWSER JAVASCRIPT ERROR:'));
console.error(chalk_1.default.red(` Message: ${message}`));
if (line)
console.error(chalk_1.default.red(` Line: ${line}, Column: ${column}`));
if (error)
console.error(chalk_1.default.red(` Error: ${error}`));
if (stack)
console.error(chalk_1.default.red(` Stack: ${stack}`));
console.error(chalk_1.default.gray(` User Agent: ${userAgent}`));
console.error(chalk_1.default.red('🔄 Suggestion: Check the browser console and fix the JavaScript syntax'));
res.status(200).json({ status: 'logged' });
});
// Main dashboard route
this.app.get('/', async (req, res) => {
await this.generateLiveReport();
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
res.sendFile(this.reportPath);
});
// Health check
this.app.get('/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
targetPath: this.targetPath,
port: this.port
});
});
// Debug endpoint
this.app.get('/debug', (req, res) => {
res.json({
config: this.config,
targetPath: this.targetPath,
port: this.port,
currentResultsCount: this.currentResults.length,
reportPath: this.reportPath
});
});
}
async generateLiveReport() {
try {
const linter = new linter_1.LWCLinter(this.config);
const results = await linter.lintDirectory(this.targetPath);
const formatter = new output_formatter_1.OutputFormatter('html');
const htmlContent = await formatter.formatWithServer(results, this.port);
await fs.writeFile(this.reportPath, htmlContent);
this.currentResults = results;
}
catch (error) {
console.error('Failed to generate live report:', error);
}
}
async start() {
return new Promise((resolve, reject) => {
this.server = this.app.listen(this.port, () => {
console.log(chalk_1.default.green(`🚀 LWC Linter Dev Server started!`));
console.log(chalk_1.default.blue(`📊 Live Dashboard: http://localhost:${this.port}`));
console.log(chalk_1.default.green(`📁 Watching: ${this.targetPath}`));
console.log(chalk_1.default.yellow(`🔧 Debug info: http://localhost:${this.port}/debug`));
console.log(chalk_1.default.gray(`💡 Press Ctrl+C to stop the server`));
resolve();
}).on('error', (err) => {
if (err.code === 'EADDRINUSE') {
this.port++;
this.start().then(resolve).catch(reject);
}
else {
reject(err);
}
});
});
}
stop() {
if (this.server) {
this.server.close();
console.log(chalk_1.default.yellow('🛑 Server stopped'));
}
}
}
exports.DevServer = DevServer;
//# sourceMappingURL=dev-server.js.map