Skip to content
This repository has been archived by the owner on Jun 21, 2023. It is now read-only.

Commit

Permalink
[ESLint] Adds --format flag to next lint (vercel#27052)
Browse files Browse the repository at this point in the history
Adds `--format` support to `next lint` to allow defining of additional formatters. Fixes vercel#26387.
  • Loading branch information
housseindjirdeh authored Jul 24, 2021
1 parent 460b9b2 commit 743bda5
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 39 deletions.
9 changes: 8 additions & 1 deletion packages/next/cli/next-lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -112,6 +114,9 @@ const nextLint: cliCommand = (argv) => {
Handling warnings:
--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
Expand Down Expand Up @@ -148,14 +153,16 @@ const nextLint: cliCommand = (argv) => {

const reportErrorsOnly = Boolean(args['--quiet'])
const maxWarnings = args['--max-warnings'] ?? -1
const formatter = args['--format'] || null

runLintCheck(
baseDir,
lintDirs,
false,
eslintOptions(args),
reportErrorsOnly,
maxWarnings
maxWarnings,
formatter
)
.then(async (lintResults) => {
const lintOutput =
Expand Down
80 changes: 47 additions & 33 deletions packages/next/lib/eslint/customFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, '/')
)
Expand All @@ -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]
Expand All @@ -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 {
Expand All @@ -80,38 +90,42 @@ 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
totalNextPluginWarningCount: number
} {
let totalNextPluginErrorCount = 0
let totalNextPluginWarningCount = 0

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')
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:
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`
Expand Down
19 changes: 14 additions & 5 deletions packages/next/lib/eslint/runLintCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -215,7 +223,8 @@ export async function runLintCheck(
pkgJsonPath,
eslintOptions,
reportErrorsOnly,
maxWarnings
maxWarnings,
formatter
)
} catch (err) {
throw err
Expand Down
18 changes: 18 additions & 0 deletions test/integration/eslint/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})
})
})

0 comments on commit 743bda5

Please sign in to comment.