UNPKG

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.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.loadAppleDesignRules = loadAppleDesignRules; function loadAppleDesignRules() { return [ { name: 'apple-spacing', description: 'Enforce Apple design system spacing guidelines', category: 'apple-design', severity: 'warn', fixable: true, check: (content, filePath, config) => { const issues = []; if (!filePath.endsWith('.css')) return issues; const lines = content.split('\n'); const appleSpacingValues = ['4px', '8px', '12px', '16px', '20px', '24px', '32px', '40px', '48px', '64px']; lines.forEach((line, index) => { // Check for margin and padding values const spacingRegex = /(margin|padding)(-[a-z]+)?:\s*([^;]+);/g; let match; while ((match = spacingRegex.exec(line)) !== null) { const value = match[3].trim(); // Skip variables and calculations if (value.includes('var(') || value.includes('calc(')) { continue; } // Check if value uses Apple spacing scale const hasValidSpacing = appleSpacingValues.some(validValue => value.includes(validValue) || value === '0'); if (!hasValidSpacing && /\d+px/.test(value)) { issues.push({ rule: 'apple-spacing', message: `Use Apple design system spacing values: ${appleSpacingValues.join(', ')}`, severity: 'warn', line: index + 1, fixable: true, category: 'apple-design' }); } } }); return issues; }, fix: (content, issues) => { let fixedContent = content; // Replace common non-standard spacing with Apple values const spacingMap = { '5px': '4px', '6px': '8px', '10px': '8px', '14px': '12px', '15px': '16px', '18px': '16px', '22px': '20px', '25px': '24px', '30px': '32px', '35px': '32px', '45px': '48px', '50px': '48px', '60px': '64px' }; Object.entries(spacingMap).forEach(([oldValue, newValue]) => { fixedContent = fixedContent.replace(new RegExp(`(margin|padding)([^:]*):([^;]*?)${oldValue}`, 'g'), `$1$2:$3${newValue}`); }); return fixedContent; } }, { name: 'apple-typography', description: 'Enforce Apple typography guidelines', category: 'apple-design', severity: 'warn', fixable: false, check: (content, filePath, config) => { const issues = []; if (!filePath.endsWith('.css')) return issues; const lines = content.split('\n'); const appleFontSizes = ['11px', '12px', '13px', '14px', '16px', '18px', '20px', '24px', '28px', '32px', '40px', '48px']; const appleFontWeights = ['300', '400', '500', '600', '700']; lines.forEach((line, index) => { // Check font-size const fontSizeMatch = line.match(/font-size:\s*([^;]+);/); if (fontSizeMatch) { const fontSize = fontSizeMatch[1].trim(); if (!appleFontSizes.includes(fontSize) && /\d+px/.test(fontSize)) { issues.push({ rule: 'apple-typography', message: `Use Apple typography scale font sizes: ${appleFontSizes.join(', ')}`, severity: 'warn', line: index + 1, fixable: false, category: 'apple-design' }); } } // Check font-weight const fontWeightMatch = line.match(/font-weight:\s*([^;]+);/); if (fontWeightMatch) { const fontWeight = fontWeightMatch[1].trim(); if (!appleFontWeights.includes(fontWeight) && fontWeight !== 'normal' && fontWeight !== 'bold') { issues.push({ rule: 'apple-typography', message: `Use Apple typography font weights: ${appleFontWeights.join(', ')}, normal, bold`, severity: 'warn', line: index + 1, fixable: false, category: 'apple-design' }); } } // Check for non-system fonts if (line.includes('font-family') && !line.includes('-apple-system') && !line.includes('BlinkMacSystemFont') && !line.includes('San Francisco')) { issues.push({ rule: 'apple-typography', message: 'Consider using Apple system fonts: -apple-system, BlinkMacSystemFont, "SF Pro"', severity: 'info', line: index + 1, fixable: false, category: 'apple-design' }); } }); return issues; } }, { name: 'apple-color-usage', description: 'Encourage use of Apple design system colors', category: 'apple-design', severity: 'info', fixable: false, check: (content, filePath, config) => { const issues = []; if (!filePath.endsWith('.css')) return issues; const lines = content.split('\n'); const appleColors = { blue: ['#007AFF', '#0051D5', '#34C759'], red: ['#FF3B30', '#D70015'], orange: ['#FF9500', '#FF6D00'], yellow: ['#FFCC00', '#FFB800'], green: ['#34C759', '#248A3D'], purple: ['#AF52DE', '#7B68EE'], gray: ['#8E8E93', '#C7C7CC', '#F2F2F7'] }; lines.forEach((line, index) => { // Check for color values const colorMatch = line.match(/(color|background-color|border-color):\s*(#[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{3});/); if (colorMatch) { const colorValue = colorMatch[2].toUpperCase(); // Check if it's a standard Apple color const isAppleColor = Object.values(appleColors).flat().some(appleColor => appleColor.toUpperCase() === colorValue); if (!isAppleColor) { issues.push({ rule: 'apple-color-usage', message: 'Consider using Apple design system colors for consistency', severity: 'info', line: index + 1, fixable: false, category: 'apple-design' }); } } }); return issues; } }, { name: 'apple-layout-hierarchy', description: 'Enforce Apple design layout hierarchy principles', category: 'apple-design', severity: 'warn', fixable: false, check: (content, filePath, config) => { const issues = []; if (!filePath.endsWith('.html')) return issues; const lines = content.split('\n'); lines.forEach((line, index) => { // Check for proper heading hierarchy const headingMatch = line.match(/<h([1-6])/); if (headingMatch) { const level = parseInt(headingMatch[1]); // Look for previous headings to check hierarchy const prevLines = lines.slice(0, index); const lastHeadingMatch = prevLines.reverse().find(l => l.match(/<h[1-6]/)); if (lastHeadingMatch) { const lastLevel = parseInt(lastHeadingMatch.match(/<h([1-6])/)?.[1] || '1'); if (level > lastLevel + 1) { issues.push({ rule: 'apple-layout-hierarchy', message: `Heading levels should not skip. Previous heading was h${lastLevel}, current is h${level}`, severity: 'warn', line: index + 1, fixable: false, category: 'apple-design' }); } } } // Check for proper button hierarchy if (line.includes('<button') && line.includes('class=')) { const hasDestructive = line.includes('destructive'); const hasPrimary = line.includes('primary'); // Count buttons in the same container const containerButtons = content.split('\n').filter(l => l.includes('<button') && l.includes('primary')).length; if (containerButtons > 1) { issues.push({ rule: 'apple-layout-hierarchy', message: 'Avoid multiple primary buttons in the same context. Use secondary buttons for additional actions', severity: 'warn', line: index + 1, fixable: false, category: 'apple-design' }); } } }); return issues; } }, { name: 'apple-responsive-design', description: 'Ensure responsive design follows Apple guidelines', category: 'apple-design', severity: 'warn', fixable: false, check: (content, filePath, config) => { const issues = []; if (!filePath.endsWith('.css')) return issues; const lines = content.split('\n'); const appleBreakpoints = ['768px', '1024px', '1280px']; lines.forEach((line, index) => { // Check for media queries if (line.includes('@media')) { const breakpointMatch = line.match(/(\d+)px/); if (breakpointMatch) { const breakpoint = breakpointMatch[1] + 'px'; if (!appleBreakpoints.includes(breakpoint)) { issues.push({ rule: 'apple-responsive-design', message: `Consider using Apple standard breakpoints: ${appleBreakpoints.join(', ')}`, severity: 'info', line: index + 1, fixable: false, category: 'apple-design' }); } } } // Check for fixed widths that might break responsiveness if (line.includes('width:') && line.includes('px') && !line.includes('min-width') && !line.includes('max-width')) { const widthMatch = line.match(/width:\s*(\d+)px/); if (widthMatch && parseInt(widthMatch[1]) > 320) { issues.push({ rule: 'apple-responsive-design', message: 'Consider using flexible units (%, vw, rem) instead of fixed pixel widths for responsive design', severity: 'warn', line: index + 1, fixable: false, category: 'apple-design' }); } } }); return issues; } } ]; } //# sourceMappingURL=apple-design.js.map