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
276 lines • 13.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadESLintPrettierRules = loadESLintPrettierRules;
function loadESLintPrettierRules() {
return [
{
name: 'eslint-integration',
description: 'Run ESLint on JavaScript files for comprehensive code analysis',
category: 'code-quality',
severity: 'warn',
fixable: true,
check: (content, filePath, config) => {
const issues = [];
if (!filePath.endsWith('.js'))
return issues;
// Basic ESLint-style checks
const lines = content.split('\n');
lines.forEach((line, index) => {
// Check for double quotes when single quotes should be used
if (line.includes('"') && !line.includes("'") && !line.trim().startsWith('//')) {
const doubleQuoteMatches = line.match(/"/g);
if (doubleQuoteMatches && doubleQuoteMatches.length >= 2) {
issues.push({
rule: 'eslint-integration',
message: 'Use single quotes instead of double quotes',
severity: 'warn',
line: index + 1,
fixable: true,
category: 'code-quality',
});
}
}
// Check for missing semicolons
if (line.trim() &&
!line.trim().startsWith('//') &&
!line.trim().startsWith('*') &&
!line.trim().startsWith('/*') &&
!line.trim().endsWith(';') &&
!line.trim().endsWith('{') &&
!line.trim().endsWith('}') &&
!line.includes('if (') &&
!line.includes('for (') &&
!line.includes('while (') &&
!line.includes('else') &&
!line.includes('try') &&
!line.includes('catch') &&
!line.includes('finally') &&
(line.includes('=') || line.includes('return') || line.includes('throw'))) {
issues.push({
rule: 'eslint-integration',
message: 'Missing semicolon at end of statement',
severity: 'error',
line: index + 1,
fixable: true,
category: 'code-quality',
});
}
// Check for == instead of ===
if (line.includes('==') && !line.includes('===') && !line.includes('!==') && !line.trim().startsWith('//')) {
issues.push({
rule: 'eslint-integration',
message: 'Use strict equality (===) instead of loose equality (==)',
severity: 'error',
line: index + 1,
fixable: true,
category: 'code-quality',
});
}
// Check for complex conditions (multiple && or ||)
const complexCondition = (line.match(/&&/g) || []).length + (line.match(/\|\|/g) || []).length;
if (complexCondition > 3) {
issues.push({
rule: 'eslint-integration',
message: 'Complex condition detected. Consider breaking into smaller parts for readability',
severity: 'warn',
line: index + 1,
fixable: false,
category: 'code-quality',
});
}
});
return issues;
},
fix: (content, issues) => {
let fixedContent = content;
issues.forEach(issue => {
if (issue.rule === 'eslint-integration' && issue.fixable) {
if (issue.message.includes('single quotes')) {
// Fix double quotes to single quotes
fixedContent = fixedContent.replace(/"/g, "'");
}
else if (issue.message.includes('semicolon')) {
// Add missing semicolons
const lines = fixedContent.split('\n');
if (issue.line && lines[issue.line - 1]) {
lines[issue.line - 1] = lines[issue.line - 1].trimEnd() + ';';
fixedContent = lines.join('\n');
}
}
else if (issue.message.includes('strict equality')) {
// Fix == to ===
fixedContent = fixedContent.replace(/([^=!])={2}([^=])/g, '$1===$2');
}
}
});
return fixedContent;
},
},
{
name: 'prettier-formatting',
description: 'Ensure consistent code formatting with Prettier standards',
category: 'code-quality',
severity: 'warn',
fixable: true,
check: (content, filePath, config) => {
const issues = [];
const lines = content.split('\n');
lines.forEach((line, index) => {
// Check for inconsistent indentation (mixing tabs and spaces)
if (line.match(/^\t+ +/) || line.match(/^ +\t/)) {
issues.push({
rule: 'prettier-formatting',
message: 'Inconsistent indentation: mixing tabs and spaces',
severity: 'warn',
line: index + 1,
fixable: true,
category: 'code-quality',
});
}
// Check for trailing spaces
if (line.match(/\s+$/)) {
issues.push({
rule: 'prettier-formatting',
message: 'Trailing whitespace detected',
severity: 'warn',
line: index + 1,
fixable: true,
category: 'code-quality',
});
}
// Check for multiple consecutive empty lines
if (line.trim() === '' && lines[index + 1]?.trim() === '' && lines[index + 2]?.trim() === '') {
issues.push({
rule: 'prettier-formatting',
message: 'Too many consecutive empty lines',
severity: 'warn',
line: index + 1,
fixable: true,
category: 'code-quality',
});
}
// Check for long lines (over 100 characters)
if (line.length > 100 && !line.trim().startsWith('//')) {
issues.push({
rule: 'prettier-formatting',
message: 'Line exceeds maximum length of 100 characters',
severity: 'info',
line: index + 1,
fixable: false,
category: 'code-quality',
});
}
// Check for missing space after commas
if (line.match(/,\S/) && !line.trim().startsWith('//')) {
issues.push({
rule: 'prettier-formatting',
message: 'Missing space after comma',
severity: 'warn',
line: index + 1,
fixable: true,
category: 'code-quality',
});
}
// Check for missing space around operators
if (line.match(/\S[+\-*/=]\S/) && !line.includes('++') && !line.includes('--') && !line.trim().startsWith('//')) {
issues.push({
rule: 'prettier-formatting',
message: 'Missing space around operators',
severity: 'warn',
line: index + 1,
fixable: true,
category: 'code-quality',
});
}
});
return issues;
},
fix: (content, issues) => {
let fixedContent = content;
// Fix trailing spaces
fixedContent = fixedContent.replace(/\s+$/gm, '');
// Fix multiple consecutive empty lines
fixedContent = fixedContent.replace(/\n{3,}/g, '\n\n');
// Fix missing space after commas
fixedContent = fixedContent.replace(/,(\S)/g, ', $1');
// Fix missing space around operators (basic cases)
fixedContent = fixedContent.replace(/(\S)([+\-*/=])(\S)/g, '$1 $2 $3');
// Convert tabs to spaces (2 spaces)
fixedContent = fixedContent.replace(/\t/g, ' ');
return fixedContent;
},
},
{
name: 'import-organization',
description: 'Ensure proper import statement organization and formatting',
category: 'code-quality',
severity: 'info',
fixable: true,
check: (content, filePath, config) => {
const issues = [];
if (!filePath.endsWith('.js'))
return issues;
const lines = content.split('\n');
const importLines = [];
lines.forEach((line, index) => {
if (line.trim().startsWith('import ')) {
importLines.push({ line: line.trim(), index });
}
});
// Check if imports are at the top of the file
if (importLines.length > 0) {
const firstImportIndex = importLines[0].index;
for (let i = 0; i < firstImportIndex; i++) {
if (lines[i].trim() && !lines[i].trim().startsWith('//') && !lines[i].trim().startsWith('/*')) {
issues.push({
rule: 'import-organization',
message: 'Import statements should be at the top of the file',
severity: 'warn',
line: firstImportIndex + 1,
fixable: false,
category: 'code-quality',
});
break;
}
}
}
// Check for unused imports (basic check)
importLines.forEach(({ line, index }) => {
const importMatch = line.match(/import\s+(?:{([^}]+)}|\*\s+as\s+(\w+)|(\w+))/);
if (importMatch) {
const importedNames = importMatch[1] ?
importMatch[1].split(',').map(name => name.trim()) :
[importMatch[2] || importMatch[3]];
importedNames.forEach(name => {
if (name && !content.includes(name.replace(/\s*as\s+\w+/, ''))) {
issues.push({
rule: 'import-organization',
message: `Unused import '${name}'`,
severity: 'warn',
line: index + 1,
fixable: true,
category: 'code-quality',
});
}
});
}
});
return issues;
},
fix: (content, issues) => {
// Basic fix: remove lines with unused imports
let fixedContent = content;
const lines = fixedContent.split('\n');
issues.forEach(issue => {
if (issue.rule === 'import-organization' && issue.message.includes('Unused import') && issue.line) {
lines[issue.line - 1] = '';
}
});
// Remove empty lines where imports were removed
fixedContent = lines.join('\n').replace(/\n\s*\n/g, '\n');
return fixedContent;
},
},
];
}
//# sourceMappingURL=eslint-prettier.js.map