Files
SSAUGenerator/docx/src/config/validate.js
StepanovPlaton bf7816ab7c Rename project to SSAU Generator and restructure content
- Move projects into content/<project>/{docx,pptx} with shared assets/
- Flatten config paths; output = project name in project folder
- Add pptx --init scaffold; remove docx exe build
- Add feature-complete content/example; ignore content/* except example
- Rewrite README and AGENTS.md
2026-10-11 23:25:22 +04:00

124 lines
4.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
const fs = require('fs');
const SECTION_TYPES = ['toc', 'structural', 'numbered', 'bibliography', 'appendix'];
const NUMBERING_SCOPES = ['global', 'section'];
function isPositiveInt(value) {
return Number.isInteger(value) && value > 0;
}
function validateConfig(config) {
const errors = [];
const { document: doc, typography, page, toc, appendix, numbering } = config;
if (!Array.isArray(config.sections) || config.sections.length === 0) {
errors.push('sections: должен быть непустой список частей документа');
} else {
const letters = new Set();
config.sections.forEach((entry, index) => {
const where = `sections[${index}]`;
if (!entry || typeof entry !== 'object') {
errors.push(`${where}: ожидался объект с полем type`);
return;
}
if (!SECTION_TYPES.includes(entry.type)) {
errors.push(
`${where}: неизвестный type "${entry.type}" (допустимо: ${SECTION_TYPES.join(', ')})`
);
return;
}
if (entry.type === 'toc') {
if (entry.file) errors.push(`${where}: часть type=toc не должна иметь file`);
return;
}
if (!entry.file) {
errors.push(`${where}: отсутствует file`);
} else if (!fs.existsSync(entry.file)) {
errors.push(`${where}: файл не найден: ${entry.file}`);
}
if (entry.type === 'structural' && !entry.title) {
errors.push(`${where}: для structural требуется title`);
}
if (entry.type === 'appendix') {
if (!entry.letter || String(entry.letter).length !== 1) {
errors.push(`${where}: для appendix требуется одиночная буква в letter`);
} else {
const letter = String(entry.letter).toUpperCase();
if (appendix.forbiddenLetters.map((l) => l.toUpperCase()).includes(letter)) {
errors.push(`${where}: буква приложения "${letter}" запрещена СТО`);
}
if (letters.has(letter)) errors.push(`${where}: повтор буквы приложения "${letter}"`);
letters.add(letter);
}
}
});
}
if (doc.titlePage && !fs.existsSync(doc.titlePage)) {
errors.push(`document.titlePage: файл не найден: ${doc.titlePage}`);
}
for (const key of ['sizeBody', 'sizeTable', 'sizeSmall']) {
if (!isPositiveInt(typography[key])) {
errors.push(`typography.${key}: ожидалось положительное целое (half-points)`);
}
}
if (!(typeof typography.lineSpacing === 'number' && typography.lineSpacing > 0)) {
errors.push('typography.lineSpacing: ожидалось положительное число');
}
if (typography.language != null && typeof typography.language !== 'string') {
errors.push('typography.language: ожидалась строка (например, ru-RU)');
}
for (const [key, value] of Object.entries(page.margins)) {
if (!Number.isInteger(value) || value < 0) {
errors.push(`page.margins.${key}: ожидалось неотрицательное целое (twips)`);
}
}
if (!isPositiveInt(toc.depth) || toc.depth > 3) {
errors.push('toc.depth: ожидалось 1..3');
}
if (typeof toc.enabled !== 'boolean') {
errors.push('toc.enabled: ожидалось true/false');
}
for (const key of ['figures', 'tables', 'formulas']) {
if (!NUMBERING_SCOPES.includes(numbering[key])) {
errors.push(`numbering.${key}: допустимо ${NUMBERING_SCOPES.join(' | ')}`);
}
}
if (config.plantuml) {
const p = config.plantuml;
if (typeof p.enabled !== 'boolean') {
errors.push('plantuml.enabled: ожидалось true/false');
}
if (p.format !== 'png') {
errors.push(`plantuml.format: поддерживается только "png", получено "${p.format}"`);
}
if (typeof p.jar !== 'string' || !p.jar) {
errors.push('plantuml.jar: ожидался путь к plantuml.jar');
}
if (typeof p.java !== 'string' || !p.java) {
errors.push('plantuml.java: ожидалось имя или путь к java (или "auto")');
}
if (p.cacheDir != null && typeof p.cacheDir !== 'string') {
errors.push('plantuml.cacheDir: ожидалась строка');
}
if (!isPositiveInt(p.timeoutMs)) {
errors.push('plantuml.timeoutMs: ожидалось положительное целое (мс)');
}
if (typeof p.cache !== 'boolean') {
errors.push('plantuml.cache: ожидалось true/false');
}
}
return errors;
}
module.exports = { validateConfig, SECTION_TYPES };