Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x | import * as path from 'path';
import * as XLSX from 'xlsx';
import type { ImportableRule, ValidationIssue } from '../import-types';
import type { Severity } from '../../types';
import { t } from '../../i18n/messages';
const REQUIRED_HEADERS = ['id', 'severity', 'description', 'message'];
const VALID_SEVERITY = ['error', 'warning', 'info'];
function splitList(v: unknown): string[] {
const s = String(v ?? '').trim();
if (!s) { return []; }
return s.split(/[,;、\n]/).map(x => x.trim()).filter(Boolean);
}
export interface TemplateParseResult {
rules: ImportableRule[];
validRules: ImportableRule[];
yamlContent: string;
skippedCount: number;
}
export function parseTemplate(srcPath: string): TemplateParseResult {
const ext = path.extname(srcPath).toLowerCase();
if (!['.xlsx', '.xls'].includes(ext)) {
throw new Error(t('import.template.badFormat'));
}
let wb: XLSX.WorkBook;
try { wb = XLSX.readFile(srcPath); }
catch { throw new Error(t('import.template.badFormat')); }
const sheet = wb.Sheets[wb.SheetNames[0]];
const rows = XLSX.utils.sheet_to_json<Record<string, string>>(sheet, { defval: '' });
if (rows.length === 0) {
throw new Error(t('import.template.empty'));
}
const header = Object.keys(rows[0]).map(k => k.trim().toLowerCase());
const missing = REQUIRED_HEADERS.filter(h => !header.includes(h));
if (missing.length > 0) {
throw new Error(t('import.template.notTemplate', { 0: missing.join(', ') }));
}
const totalRows = rows.length;
const rules: ImportableRule[] = rows
.map((r, idx) => ({ r, rowNo: idx + 2 }))
.filter(({ r }) => {
const id = String(r.id ?? '').trim();
const description = String(r.description ?? '').trim();
const message = String(r.message ?? '').trim();
return id !== '' || description !== '' || message !== '';
})
.map(({ r, rowNo }) => {
const issues: ValidationIssue[] = [];
const rawId = String(r.id ?? '').trim();
let id = rawId;
let idPlaceholder = false;
if (!rawId) {
id = `rule-${rowNo}`;
idPlaceholder = true;
issues.push({ field: 'id', severity: 'error', message: t('import.idMissing') });
}
const sevRaw = String(r.severity ?? '').trim().toLowerCase();
const originalSeverity = String(r.severity ?? '').trim();
const severity: Severity = VALID_SEVERITY.includes(sevRaw) ? (sevRaw as Severity) : 'warning';
if (!VALID_SEVERITY.includes(sevRaw)) {
const detail = originalSeverity ? `: "${originalSeverity}"` : '';
issues.push({ field: 'severity', severity: 'warning', message: `${t('import.validationSeverityInvalid')}${detail}` });
}
const description = String(r.description ?? '').trim();
if (!description) {
issues.push({ field: 'description', severity: 'error', message: 'description 为空' });
}
const message = String(r.message ?? '').trim();
if (!message) {
issues.push({ field: 'message', severity: 'error', message: 'message 为空' });
}
return {
id,
severity,
description,
message,
languages: splitList(r.languages),
excludeLanguages: splitList(r.excludeLanguages),
rowNumber: rowNo,
originalSeverity,
idPlaceholder,
validationIssues: issues.length > 0 ? issues : undefined,
};
});
const validRules = rules.filter(r => !r.validationIssues);
const yamlContent = buildYaml(validRules);
const skippedCount = totalRows - rules.length;
return { rules, validRules, yamlContent, skippedCount };
}
function buildYaml(rules: ImportableRule[]): string {
const lines: string[] = [];
for (const r of rules) {
lines.push(`- id: ${r.id}`);
lines.push(` severity: ${r.severity}`);
lines.push(` description: ${r.description}`);
lines.push(` message: ${r.message}`);
if (r.languages?.length) {
lines.push(` languages: [${r.languages.join(', ')}]`);
}
if (r.excludeLanguages?.length) {
lines.push(` excludeLanguages: [${r.excludeLanguages.join(', ')}]`);
}
}
return lines.join('\n');
}
|