import chalk from 'next/dist/compiled/chalk' import path from 'path' // eslint-disable-next-line no-shadow export enum MessageSeverity { Warning = 1, Error = 2, } interface LintMessage { ruleId: string | null severity: 1 | 2 message: string line: number column: number } export interface LintResult { filePath: string messages: LintMessage[] errorCount: number warningCount: number output?: string source?: string } function pluginCount(messages: LintMessage[]): { nextPluginErrorCount: number nextPluginWarningCount: number } { let nextPluginWarningCount = 0 let nextPluginErrorCount = 0 for (let i = 0; i < messages.length; i++) { const { severity, ruleId } = messages[i] if (ruleId?.includes('@next/next')) { if (severity === MessageSeverity.Warning) { nextPluginWarningCount += 1 } else { nextPluginErrorCount += 1 } } } return { nextPluginErrorCount, nextPluginWarningCount, } } function formatMessage( dir: string, messages: LintMessage[], filePath: string ): string { let fileName = path.posix.normalize( path.relative(dir, filePath).replace(/\\/g, '/') ) if (!fileName.startsWith('.')) { fileName = './' + fileName } let output = '\n' + chalk.cyan(fileName) for (let i = 0; i < messages.length; i++) { const { message, severity, line, column, ruleId } = messages[i] output = output + '\n' if (line && column) { output = output + chalk.yellow(line.toString()) + ':' + chalk.yellow(column.toString()) + ' ' } if (severity === MessageSeverity.Warning) { output += chalk.yellow.bold('Warning') + ': ' } else { output += chalk.red.bold('Error') + ': ' } output += message if (ruleId) { output += ' ' + chalk.gray.bold(ruleId) } } return output } export function formatResults( baseDir: string, results: LintResult[], format: (r: LintResult[]) => string ): { output: string totalNextPluginErrorCount: number totalNextPluginWarningCount: number } { let totalNextPluginErrorCount = 0 let totalNextPluginWarningCount = 0 let resultsWithMessages = results.filter(({ messages }) => messages?.length) // Track number of Next.js plugin errors and warnings resultsWithMessages.forEach(({ messages }) => { const res = pluginCount(messages) totalNextPluginErrorCount += res.nextPluginErrorCount totalNextPluginWarningCount += res.nextPluginWarningCount }) // Use user defined formatter or Next.js's built-in custom formatter const output = format ? format(resultsWithMessages) : resultsWithMessages .map(({ messages, filePath }) => formatMessage(baseDir, messages, filePath) ) .join('\n') return { output: resultsWithMessages.length > 0 ? output + `\n\n${chalk.cyan( 'info' )} - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules` : '', totalNextPluginErrorCount, totalNextPluginWarningCount, } }