UNPKG

@vrcd-community/zhlint

Version:

A linting tool for Chinese language.

124 lines (123 loc) 5.02 kB
var _a, _b; import chalk from 'chalk'; import { CharType, checkCharType, isFullwidthPunctuationType, isHalfwidthPunctuationType } from './parser/index.js'; export const env = { stdout: (_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.process) === null || _a === void 0 ? void 0 : _a.stdout, stderr: (_b = globalThis === null || globalThis === void 0 ? void 0 : globalThis.process) === null || _b === void 0 ? void 0 : _b.stderr, defaultLogger: console }; if (globalThis.__DEV__) { // eslint-disable-next-line @typescript-eslint/no-var-requires const fs = require('fs'); // eslint-disable-next-line @typescript-eslint/no-var-requires const { Console: NativeConsole } = require('console'); env.stdout = fs.createWriteStream('./stdout.log', { encoding: 'utf-8' }); env.stderr = fs.createWriteStream('./stderr.log', { encoding: 'utf-8' }); env.defaultLogger = new NativeConsole(env.stdout, env.stderr); } const getPositionByOffset = (str, offset) => { const rows = str.split('\n'); const rowLengthList = rows.map((substr) => substr.length); const position = { offset, row: 0, column: 0, line: '' }; while (position.offset >= 0 && rows.length) { position.row++; position.column = position.offset; position.line = rows.shift() || ''; position.offset -= (rowLengthList.shift() || 0) + 1; } return position; }; export var ValidationTarget; (function (ValidationTarget) { ValidationTarget["VALUE"] = "value"; ValidationTarget["START_VALUE"] = "startValue"; ValidationTarget["END_VALUE"] = "endValue"; ValidationTarget["SPACE_AFTER"] = "spaceAfter"; ValidationTarget["INNER_SPACE_BEFORE"] = "innerSpaceBefore"; })(ValidationTarget || (ValidationTarget = {})); const adjustedFullwidthPunctuations = `“”‘’`; const generateMarker = (str, index) => { const prefix = str.substring(0, index); let fullwidthCount = 0; let halfwidthCount = 0; for (let i = 0; i < prefix.length; i++) { const charType = checkCharType(prefix[i]); if (charType === CharType.CJK_CHAR || (isFullwidthPunctuationType(charType) && adjustedFullwidthPunctuations.indexOf(prefix[i]) === -1)) { fullwidthCount++; } else if (charType === CharType.WESTERN_LETTER || (isHalfwidthPunctuationType(charType) && adjustedFullwidthPunctuations.indexOf(prefix[i]) !== -1) || charType === CharType.SPACE) { halfwidthCount++; } } return (' '.repeat(halfwidthCount) + ' '.repeat(fullwidthCount) + `${chalk.red('^')}`); }; export const reportItem = (file = '', str, validations, logger = env.defaultLogger) => { validations.forEach(({ index, length, target, message }) => { // 0. final index and position const finalIndex = target === 'spaceAfter' || target === 'endValue' ? index + length : index; const { row, column, line } = getPositionByOffset(str, finalIndex); // 1. headline const fileDisplay = `${chalk.blue.bgWhite(file)}${file ? ':' : ''}`; const positionDisplay = `${chalk.yellow(row)}:${chalk.yellow(column)}`; const headline = `${fileDisplay}${positionDisplay} - ${message}`; // 2. display fragment const displayRange = 20; const displayStart = column - displayRange < 0 ? 0 : column - displayRange; const displayEnd = column + length + displayRange > line.length - 1 ? line.length : column + length + displayRange; const displayFragment = line .substring(displayStart, displayEnd) .replace(/\n/g, '\\n'); // 3. marker below const markerBelow = generateMarker(displayFragment, column - displayStart); logger.error(`${headline}\n\n${displayFragment}\n${markerBelow}\n`); }); }; export const report = (resultList, logger = env.defaultLogger) => { let errorCount = 0; const invalidFiles = []; resultList .filter(({ file, disabled }) => { if (disabled) { if (file) { logger.log(`${chalk.blue.bgWhite(file)}: disabled`); } else { logger.log(`disabled`); } return false; } return true; }) .forEach(({ file, origin, validations }) => { reportItem(file, origin, validations, logger); errorCount += validations.length; if (file && validations.length) { invalidFiles.push(file); } }); if (errorCount) { const errors = []; errors.push('Invalid files:'); errors.push('- ' + invalidFiles.join('\n- ') + '\n'); errors.push(`Found ${errorCount} ${errorCount > 1 ? 'errors' : 'error'}.`); logger.error(errors.join('\n')); return 1; } else { logger.log(`No error found.`); } };