[ESLint] Adds --format flag to next lint (#27052)

Adds `--format` support to `next lint` to allow defining of additional formatters. Fixes #26387.
This commit is contained in:
Houssein Djirdeh 2021-07-23 21:35:56 -04:00 committed by GitHub
parent fac083bcfa
commit 078cfb59f9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 38 deletions

View file

@ -63,9 +63,11 @@ const nextLint: cliCommand = (argv) => {
'--cache': Boolean,
'--cache-location': String,
'--error-on-unmatched-pattern': Boolean,
'--format': String,
// Aliases
'-c': '--config',
'-f': '--format',
}
let args: arg.Result<arg.Spec>
@ -113,6 +115,9 @@ const nextLint: cliCommand = (argv) => {
--quiet Report errors only - default: false
--max-warnings Int Number of warnings to trigger nonzero exit code - default: -1
Output:
-f, --format String Use a specific output format - default: Next.js custom formatter
Inline configuration comments:
--no-inline-config Prevent comments from changing config or rules
--report-unused-disable-directives Adds reported errors for unused eslint-disable directives ("error" | "warn" | "off")
@ -148,6 +153,7 @@ const nextLint: cliCommand = (argv) => {
const reportErrorsOnly = Boolean(args['--quiet'])
const maxWarnings = args['--max-warnings'] ?? -1
const formatter = args['--format'] || null
runLintCheck(
baseDir,
@ -155,7 +161,8 @@ const nextLint: cliCommand = (argv) => {
false,
eslintOptions(args),
reportErrorsOnly,
maxWarnings
maxWarnings,
formatter
)
.then(async (lintResults) => {
const lintOutput =

View file

@ -24,15 +24,35 @@ export interface LintResult {
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
): {
output: string
nextPluginErrorCount: number
nextPluginWarningCount: number
} {
): string {
let fileName = path.posix.normalize(
path.relative(dir, filePath).replace(/\\/g, '/')
)
@ -42,8 +62,6 @@ function formatMessage(
}
let output = '\n' + chalk.cyan(fileName)
let nextPluginWarningCount = 0
let nextPluginErrorCount = 0
for (let i = 0; i < messages.length; i++) {
const { message, severity, line, column, ruleId } = messages[i]
@ -59,14 +77,6 @@ function formatMessage(
' '
}
if (ruleId?.includes('@next/next')) {
if (severity === MessageSeverity.Warning) {
nextPluginWarningCount += 1
} else {
nextPluginErrorCount += 1
}
}
if (severity === MessageSeverity.Warning) {
output += chalk.yellow.bold('Warning') + ': '
} else {
@ -80,16 +90,13 @@ function formatMessage(
}
}
return {
output,
nextPluginErrorCount,
nextPluginWarningCount,
}
return output
}
export function formatResults(
baseDir: string,
results: LintResult[]
results: LintResult[],
format: (r: LintResult[]) => string
): {
output: string
totalNextPluginErrorCount: number
@ -97,21 +104,28 @@ export function formatResults(
} {
let totalNextPluginErrorCount = 0
let totalNextPluginWarningCount = 0
let resultsWithMessages = results.filter(({ messages }) => messages?.length)
const formattedResults = results
.filter(({ messages }) => messages?.length)
.map(({ messages, filePath }) => {
const res = formatMessage(baseDir, messages, filePath)
totalNextPluginErrorCount += res.nextPluginErrorCount
totalNextPluginWarningCount += res.nextPluginWarningCount
return res.output
})
.join('\n')
// 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:
formattedResults.length > 0
? formattedResults +
resultsWithMessages.length > 0
? output +
`\n\n${chalk.bold(
'Need to disable some ESLint rules? Learn more here:'
)} https://nextjs.org/docs/basic-features/eslint#disabling-rules\n`

View file

@ -30,7 +30,8 @@ async function lint(
pkgJsonPath: string | null,
eslintOptions: any = null,
reportErrorsOnly: boolean = false,
maxWarnings: number = -1
maxWarnings: number = -1,
formatter: string | null = null
): Promise<
| string
| null
@ -111,12 +112,18 @@ async function lint(
}
}
const lintStart = process.hrtime()
let results = await eslint.lintFiles(lintDirs)
let selectedFormatter = null
if (options.fix) await ESLint.outputFixes(results)
if (reportErrorsOnly) results = await ESLint.getErrorResults(results) // Only return errors if --quiet flag is used
const formattedResult = formatResults(baseDir, results)
if (formatter) selectedFormatter = await eslint.loadFormatter(formatter)
const formattedResult = formatResults(
baseDir,
results,
selectedFormatter?.format
)
const lintEnd = process.hrtime(lintStart)
const totalWarnings = results.reduce(
(sum: number, file: LintResult) => sum + file.warningCount,
@ -152,7 +159,8 @@ export async function runLintCheck(
lintDuringBuild: boolean = false,
eslintOptions: any = null,
reportErrorsOnly: boolean = false,
maxWarnings: number = -1
maxWarnings: number = -1,
formatter: string | null = null
): ReturnType<typeof lint> {
try {
// Find user's .eslintrc file
@ -215,7 +223,8 @@ export async function runLintCheck(
pkgJsonPath,
eslintOptions,
reportErrorsOnly,
maxWarnings
maxWarnings,
formatter
)
} catch (err) {
throw err

View file

@ -305,5 +305,23 @@ describe('ESLint', () => {
'Warning: External synchronous scripts are forbidden'
)
})
test('format flag supports additional user-defined formats', async () => {
const { stdout, stderr } = await nextLint(
dirMaxWarnings,
['-f', 'codeframe'],
{
stdout: true,
stderr: true,
}
)
const output = stdout + stderr
expect(output).toContain(
'warning: External synchronous scripts are forbidden'
)
expect(stdout).toContain('<script src="https://example.com" />')
expect(stdout).toContain('2 warnings found')
})
})
})