UNPKG

gplint

Version:

A Gherkin linter/validator written in Javascript.

112 lines 4.13 kB
import _ from 'lodash'; import { featureSpread } from './utils/gherkin.js'; const TABLE_SEPARATOR = '|'; const TABLE_SPLITTER = /(?<!\\)\|/; export const name = 'table-align'; export const availableConfigs = { examples: true, steps: true, }; export function run({ feature, file }, configuration) { function _checkRows(rows) { // row could be null on missing tables if (rows.length === 0 || rows.some(row => row == null)) { return; } const tableLines = rows.map(row => _splitTableRow(file.lines[row.location.line - 1])); const columnsCount = tableLines[0].length; const columns = _.range(columnsCount).map(i => tableLines.map(row => row[i])); const columnsMaxLength = columns.map(column => Math.max(...column.map(cell => cell.trim().length))); rows.forEach((row, rowIndex) => { const realLine = _.trim(file.lines[row.location.line - 1].trim(), TABLE_SEPARATOR); const realCells = realLine.split(TABLE_SPLITTER); row.cells.forEach((cell, cellIndex) => { const cellValue = tableLines[rowIndex][cellIndex].trim(); const expectedCellValue = ` ${cellValue.padEnd(columnsMaxLength[cellIndex])} `; if (expectedCellValue !== realCells[cellIndex]) { errors.push(createError({ location: cell.location, value: cellValue })); } }); }); } if (!feature) { return []; } const mergedConfig = _.merge({}, availableConfigs, configuration); const errors = []; const { children } = featureSpread(feature); for (const { scenario, background } of children) { if (mergedConfig.steps) { const tableSteps = (scenario ?? background).steps.filter(step => step.dataTable != null); for (const step of tableSteps) { _checkRows(step.dataTable.rows); } } if (mergedConfig.examples && scenario?.examples != null) { for (const example of scenario.examples) { _checkRows([example.tableHeader, ...example.tableBody]); } } } return errors; } function createError(cell) { return { message: `Cell with value "${cell.value}" is not aligned`, rule: name, line: cell.location.line, column: cell.location.column, }; } function _splitTableRow(line) { const tableRow = line.trim(); const result = []; let current = ''; let escapeCount = 0; for (const char of tableRow) { if (char === '\\') { escapeCount++; current += char; } else if (char === '|' && escapeCount % 2 === 0) { result.push(current); current = ''; escapeCount = 0; } else { current += char; escapeCount = 0; } } result.push(current); return result .slice(1, -1); // Remove the first and last elements, which are always empty due to leading and trailing '|' } export const documentation = { description: 'Allows to force table alignment on steps and/or examples. Is possible to specify if you want to apply this rule for tables on steps and/or examples', configuration: [{ name: 'examples', type: 'boolean', description: 'If sets to true, tables on examples should be aligned.', default: availableConfigs.steps, }, { name: 'steps', type: 'boolean', description: 'If sets to true, tables on steps should be aligned.', default: availableConfigs.steps, }], examples: [{ title: 'Example', description: 'Force tables on steps and examples to be properly aligned.', config: { [name]: ['error', { steps: true, examples: true, }], } }], }; //# sourceMappingURL=table-align.js.map