From b8961c8142136f8f75690801cb6bb3c03b58ceb5 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Mon, 12 Jun 2023 14:14:54 +0200 Subject: [PATCH] Change to improve how note are displayed --- lib/index.js | 431 +++++++++++++++++++++++++++------------------------ test.js | 3 +- 2 files changed, 230 insertions(+), 204 deletions(-) diff --git a/lib/index.js b/lib/index.js index 7cf378d..aed9c9d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -128,182 +128,6 @@ export function reporter(files, options) { ) } -/** - * @param {State} state - * Info passed around. - * @param {Readonly>} files - * Files. - * @returns {Array | string>} - * Rows. - */ -function createRows(state, files) { - // To do: when Node 18 is EOL, use `toSorted`. - const sortedFiles = [...files].sort(compareFile) - /** @type {Array} */ - const all = [] - let index = -1 - /** @type {Array | string>} */ - const rows = [] - let lastWasMessage = false - - while (++index < sortedFiles.length) { - const file = sortedFiles[index] - // To do: when Node 18 is EOL, use `toSorted`. - const messages = [...file.messages].sort(compareMessage) - /** @type {Array | string>} */ - const messageRows = [] - let offset = -1 - - while (++offset < messages.length) { - const message = messages[offset] - - if (!state.silent || message.fatal) { - all.push(message) - messageRows.push(...createMessageLine(state, message)) - } - } - - if ((!state.quiet && !state.silent) || messageRows.length > 0) { - const line = createFileLine(state, file) - - // EOL between message and a file header. - if (lastWasMessage && line) rows.push('') - if (line) rows.push(line) - if (messageRows.length > 0) rows.push(...messageRows) - - lastWasMessage = messageRows.length > 0 - } - } - - const stats = statistics(all) - - if (stats.fatal || stats.warn) { - rows.push('', createByline(state, stats)) - } - - return rows -} - -/** - * @param {Readonly> | string>>} rows - * Rows. - * @returns {string} - * Report. - */ -function serializeRows(rows) { - /** @type {Array} */ - const sizes = [] - let index = -1 - - // Calculate sizes. - while (++index < rows.length) { - const row = rows[index] - - if (typeof row === 'string') { - // Continue. - } else { - let cellIndex = -1 - while (++cellIndex < row.length) { - const current = sizes[cellIndex] || 0 - const size = stringWidth(row[cellIndex]) - if (size > current) { - sizes[cellIndex] = size - } - } - } - } - - /** @type {Array} */ - const lines = [] - index = -1 - - while (++index < rows.length) { - const row = rows[index] - let line = '' - - if (typeof row === 'string') { - line = row - } else { - let cellIndex = -1 - - while (++cellIndex < row.length) { - const cell = row[cellIndex] || '' - const max = (sizes[cellIndex] || 0) + 2 - line += cell + ' '.repeat(max - stringWidth(cell)) - } - } - - lines.push(line.trimEnd()) - } - - return lines.join('\n') -} - -/** - * Show a problem. - * - * @param {Readonly} state - * Info passed around. - * @param {Readonly} message - * Message. - * @returns {Array | string>} - * Line. - */ -function createMessageLine(state, message) { - const label = createLabel(message.fatal) - let reason = - (message.stack || message.message) + - (state.verbose && message.note ? '\n' + message.note : '') - - const match = eol.exec(reason) - /** @type {Array} */ - let rest = [] - - if (match) { - rest = reason.slice(match.index + 1).split(eol) - reason = reason.slice(0, match.index) - } - - const row = [ - '', - stringifyPosition(message.place), - (label === 'error' ? state.red : state.yellow) + label + state.defaultColor, - reason, - message.ruleId || '', - message.source || '' - ] - - if (message.cause) { - rest.push(...createCauseLines(state, message.cause)) - } - - if (message.ancestors) { - rest.push(...createAncestorsLines(state, message.ancestors)) - } - - return [row, ...rest] -} - -/** - * Create lines for cause. - * - * @param {State} state - * Info passed around. - * @param {NonNullable} cause - * Cause. - * @returns {Array} - * Lines. - */ -function createCauseLines(state, cause) { - const lines = [' ' + state.bold + '[cause]' + state.normalIntensity + ':'] - /* c8 ignore next -- stacks can be missing for weird reasons or in weird places. */ - const stackLines = (cause.stack || cause.message).split(eol) - stackLines[0] = ' ' + stackLines[0] - lines.push(...stackLines) - - return lines -} - /** * Create lines for ancestors. * @@ -357,6 +181,67 @@ function createAncestorsLines(state, ancestors) { return lines } +/** + * Create a summary of total problems. + * + * @param {Readonly} state + * Info passed around. + * @param {Readonly} stats + * Statistics. + * @returns {string} + * Line. + */ +function createByline(state, stats) { + let result = '' + + if (stats.fatal) { + result = + state.red + + '✖' + + state.defaultColor + + ' ' + + stats.fatal + + ' ' + + (fatalToLabel(true) + (stats.fatal === 1 ? '' : 's')) + } + + if (stats.warn) { + result = + (result ? result + ', ' : '') + + (state.yellow + '⚠' + state.defaultColor) + + ' ' + + stats.warn + + ' ' + + (fatalToLabel(false) + (stats.warn === 1 ? '' : 's')) + } + + if (stats.total !== stats.fatal && stats.total !== stats.warn) { + result = stats.total + ' messages (' + result + ')' + } + + return result +} + +/** + * Create lines for cause. + * + * @param {State} state + * Info passed around. + * @param {NonNullable} cause + * Cause. + * @returns {Array} + * Lines. + */ +function createCauseLines(state, cause) { + const lines = [' ' + state.bold + '[cause]' + state.normalIntensity + ':'] + /* c8 ignore next -- stacks can be missing for weird reasons or in weird places. */ + const stackLines = (cause.stack || cause.message).split(eol) + stackLines[0] = ' ' + stackLines[0] + lines.push(...stackLines) + + return lines +} + /** * Create a summary of problems for a file. * @@ -397,44 +282,129 @@ function createFileLine(state, file) { } /** - * Create a summary of total problems. + * Create lines for cause. + * + * @param {State} state + * Info passed around. + * @param {NonNullable} note + * Cause. + * @returns {Array} + * Lines. + */ +function createNoteLines(state, note) { + const noteLines = note.split(eol) + let index = -1 + while (++index < noteLines.length) { + noteLines[index] = ' ' + noteLines[index] + } + + return [ + ' ' + state.bold + '[note]' + state.normalIntensity + ':', + ...noteLines + ] +} + +/** + * Show a problem. * * @param {Readonly} state * Info passed around. - * @param {Readonly} stats - * Statistics. - * @returns {string} + * @param {Readonly} message + * Message. + * @returns {Array | string>} * Line. */ -function createByline(state, stats) { - let result = '' +function createMessageLine(state, message) { + const label = fatalToLabel(message.fatal) + let reason = message.stack || message.message - if (stats.fatal) { - result = - state.red + - '✖' + - state.defaultColor + - ' ' + - stats.fatal + - ' ' + - (createLabel(true) + (stats.fatal === 1 ? '' : 's')) + const match = eol.exec(reason) + /** @type {Array} */ + let rest = [] + + if (match) { + rest = reason.slice(match.index + 1).split(eol) + reason = reason.slice(0, match.index) } - if (stats.warn) { - result = - (result ? result + ', ' : '') + - (state.yellow + '⚠' + state.defaultColor) + - ' ' + - stats.warn + - ' ' + - (createLabel(false) + (stats.warn === 1 ? '' : 's')) + const row = [ + '', + stringifyPosition(message.place), + (label === 'error' ? state.red : state.yellow) + label + state.defaultColor, + reason, + message.ruleId || '', + message.source || '' + ] + + if (state.verbose && message.note) { + rest.push(...createNoteLines(state, message.note)) } - if (stats.total !== stats.fatal && stats.total !== stats.warn) { - result = stats.total + ' messages (' + result + ')' + if (message.cause) { + rest.push(...createCauseLines(state, message.cause)) } - return result + if (message.ancestors) { + rest.push(...createAncestorsLines(state, message.ancestors)) + } + + return [row, ...rest] +} + +/** + * @param {State} state + * Info passed around. + * @param {Readonly>} files + * Files. + * @returns {Array | string>} + * Rows. + */ +function createRows(state, files) { + // To do: when Node 18 is EOL, use `toSorted`. + const sortedFiles = [...files].sort(compareFile) + /** @type {Array} */ + const all = [] + let index = -1 + /** @type {Array | string>} */ + const rows = [] + let lastWasMessage = false + + while (++index < sortedFiles.length) { + const file = sortedFiles[index] + // To do: when Node 18 is EOL, use `toSorted`. + const messages = [...file.messages].sort(compareMessage) + /** @type {Array | string>} */ + const messageRows = [] + let offset = -1 + + while (++offset < messages.length) { + const message = messages[offset] + + if (!state.silent || message.fatal) { + all.push(message) + messageRows.push(...createMessageLine(state, message)) + } + } + + if ((!state.quiet && !state.silent) || messageRows.length > 0) { + const line = createFileLine(state, file) + + // EOL between message and a file header. + if (lastWasMessage && line) rows.push('') + if (line) rows.push(line) + if (messageRows.length > 0) rows.push(...messageRows) + + lastWasMessage = messageRows.length > 0 + } + } + + const stats = statistics(all) + + if (stats.fatal || stats.warn) { + rows.push('', createByline(state, stats)) + } + + return rows } /** @@ -445,6 +415,61 @@ function createByline(state, stats) { * @returns {string} * Label. */ -function createLabel(value) { +function fatalToLabel(value) { return value ? 'error' : value === false ? 'warning' : 'info' } + +/** + * @param {Readonly> | string>>} rows + * Rows. + * @returns {string} + * Report. + */ +function serializeRows(rows) { + /** @type {Array} */ + const sizes = [] + let index = -1 + + // Calculate sizes. + while (++index < rows.length) { + const row = rows[index] + + if (typeof row === 'string') { + // Continue. + } else { + let cellIndex = -1 + while (++cellIndex < row.length) { + const current = sizes[cellIndex] || 0 + const size = stringWidth(row[cellIndex]) + if (size > current) { + sizes[cellIndex] = size + } + } + } + } + + /** @type {Array} */ + const lines = [] + index = -1 + + while (++index < rows.length) { + const row = rows[index] + let line = '' + + if (typeof row === 'string') { + line = row + } else { + let cellIndex = -1 + + while (++cellIndex < row.length) { + const cell = row[cellIndex] || '' + const max = (sizes[cellIndex] || 0) + 2 + line += cell + ' '.repeat(max - stringWidth(cell)) + } + } + + lines.push(line.trimEnd()) + } + + return lines.join('\n') +} diff --git a/test.js b/test.js index b0d9cb3..fc92592 100644 --- a/test.js +++ b/test.js @@ -319,7 +319,8 @@ test('reporter', async function () { 'a.js', ' warning ...and some more warnings', ' warning Whoops', - 'Lorem ipsum dolor sit amet.', + ' [note]:', + ' Lorem ipsum dolor sit amet.', '', '⚠ 2 warnings' ].join('\n'),