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
231 lines • 10.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadPerformanceRules = loadPerformanceRules;
function loadPerformanceRules() {
return [
{
name: 'dom-query-optimization',
description: 'Optimize DOM queries and avoid repeated querySelector calls',
category: 'performance',
severity: 'warn',
fixable: false,
check: (content, filePath, config) => {
const issues = [];
if (!filePath.endsWith('.js'))
return issues;
const lines = content.split('\n');
const querySelectors = {};
lines.forEach((line, index) => {
// Find querySelector calls
const querySelectorMatch = line.match(/querySelector\(['"`]([^'"`]+)['"`]\)/);
if (querySelectorMatch) {
const selector = querySelectorMatch[1];
if (!querySelectors[selector]) {
querySelectors[selector] = [];
}
querySelectors[selector].push(index + 1);
}
});
// Check for repeated selectors
Object.entries(querySelectors).forEach(([selector, lineNumbers]) => {
if (lineNumbers.length > 2) {
issues.push({
rule: 'dom-query-optimization',
message: `Selector '${selector}' is used ${lineNumbers.length} times. Consider caching the result`,
severity: 'warn',
line: lineNumbers[0],
fixable: false,
category: 'performance'
});
}
});
// Check for querySelector inside loops
lines.forEach((line, index) => {
if ((line.includes('for (') || line.includes('forEach') || line.includes('while (')) &&
content.substring(content.indexOf(line)).includes('querySelector')) {
issues.push({
rule: 'dom-query-optimization',
message: 'Avoid DOM queries inside loops. Cache selectors outside the loop',
severity: 'warn',
line: index + 1,
fixable: false,
category: 'performance'
});
}
});
return issues;
}
},
{
name: 'reactive-property-usage',
description: 'Ensure proper usage of @track and @api decorators',
category: 'performance',
severity: 'warn',
fixable: false,
check: (content, filePath, config) => {
const issues = [];
if (!filePath.endsWith('.js'))
return issues;
const lines = content.split('\n');
lines.forEach((line, index) => {
// Check for @track on primitive properties
if (line.includes('@track') &&
(line.includes('= ""') || line.includes('= 0') || line.includes('= false') || line.includes('= true'))) {
issues.push({
rule: 'reactive-property-usage',
message: '@track is not needed for primitive values that are reassigned. Use @track only for objects/arrays',
severity: 'info',
line: index + 1,
fixable: false,
category: 'performance'
});
}
// Check for missing @track on objects/arrays
if ((line.includes('= {}') || line.includes('= []')) &&
!lines[index - 1]?.includes('@track') &&
!line.includes('@track')) {
issues.push({
rule: 'reactive-property-usage',
message: 'Consider using @track for object/array properties that will be mutated',
severity: 'info',
line: index + 1,
fixable: false,
category: 'performance'
});
}
});
return issues;
}
},
{
name: 'large-dom-check',
description: 'Warn about potentially large DOM structures',
category: 'performance',
severity: 'warn',
fixable: false,
check: (content, filePath, config) => {
const issues = [];
if (!filePath.endsWith('.html'))
return issues;
const maxDomNodes = config.performance?.maxDomNodes || 1000;
// Count DOM elements (simplified)
const elementCount = (content.match(/<[^/][^>]*>/g) || []).length;
if (elementCount > maxDomNodes) {
issues.push({
rule: 'large-dom-check',
message: `Template has ${elementCount} elements, which exceeds the recommended maximum of ${maxDomNodes}. Consider virtualization or pagination`,
severity: 'warn',
line: 1,
fixable: false,
category: 'performance'
});
}
// Check for large lists without virtualization
const lines = content.split('\n');
lines.forEach((line, index) => {
if (line.includes('for:each') && !content.includes('lightning-datatable')) {
issues.push({
rule: 'large-dom-check',
message: 'Consider using lightning-datatable or virtualization for large lists',
severity: 'info',
line: index + 1,
fixable: false,
category: 'performance'
});
}
});
return issues;
}
},
{
name: 'bundle-size-check',
description: 'Check for potential bundle size issues',
category: 'performance',
severity: 'info',
fixable: false,
check: (content, filePath, config) => {
const issues = [];
if (!filePath.endsWith('.js'))
return issues;
const maxBundleSize = config.performance?.maxBundleSize || 500000;
const contentSize = Buffer.byteLength(content, 'utf8');
if (contentSize > maxBundleSize) {
issues.push({
rule: 'bundle-size-check',
message: `File size (${Math.round(contentSize / 1024)}KB) exceeds recommended maximum. Consider code splitting`,
severity: 'info',
line: 1,
fixable: false,
category: 'performance'
});
}
// Check for large imports
const lines = content.split('\n');
lines.forEach((line, index) => {
if (line.includes('import') && line.includes('*')) {
issues.push({
rule: 'bundle-size-check',
message: 'Avoid wildcard imports. Import only what you need to reduce bundle size',
severity: 'info',
line: index + 1,
fixable: false,
category: 'performance'
});
}
});
return issues;
}
},
{
name: 'async-operation-check',
description: 'Ensure proper handling of asynchronous operations',
category: 'performance',
severity: 'warn',
fixable: false,
check: (content, filePath, config) => {
const issues = [];
if (!filePath.endsWith('.js'))
return issues;
const lines = content.split('\n');
lines.forEach((line, index) => {
// Check for synchronous operations that could block
if (line.includes('XMLHttpRequest') && !line.includes('async')) {
issues.push({
rule: 'async-operation-check',
message: 'Use async/await or fetch() instead of synchronous XMLHttpRequest',
severity: 'warn',
line: index + 1,
fixable: false,
category: 'performance'
});
}
// Check for missing await on async operations
if (line.includes('fetch(') && !line.includes('await') && !line.includes('.then(')) {
issues.push({
rule: 'async-operation-check',
message: 'Fetch calls should use await or .then() for proper async handling',
severity: 'warn',
line: index + 1,
fixable: false,
category: 'performance'
});
}
// Check for heavy operations in render cycles
if ((line.includes('renderedCallback') || line.includes('render()')) &&
(line.includes('sort(') || line.includes('filter(') || line.includes('map('))) {
issues.push({
rule: 'async-operation-check',
message: 'Avoid heavy computations in render cycles. Consider memoization or computed properties',
severity: 'warn',
line: index + 1,
fixable: false,
category: 'performance'
});
}
});
return issues;
}
}
];
}
//# sourceMappingURL=performance.js.map