/
ivanby
/
Compote
Обзор
Документация
Войти
/
ivanby
/
Compote
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
1
CI/CD
Аналитика
Безопасность
master
yaga/define.js
331 строка
12 KB
Иван Быков
Initial commit
19 июн 2026, 08:50
19 июн 2026, 08:50
b4391e5
Код
Авторство
О чём код?
#!/usr/bin/env node /* jshint evil: true */ const toolName = 'define -> '; //*** начало общий код для всех утилит const fs = require('node:fs'); const path = require('node:path'); const process = require('node:process'); const yaml = require('yaml'); let indicatorCurPos = 0; function indicator() { const indicatorChars = ['|', '/', '–', '\\']; if(indicatorCurPos < indicatorChars.length) indicatorCurPos++; if(indicatorCurPos >= indicatorChars.length) indicatorCurPos = 0; return indicatorChars[indicatorCurPos]; } const projectDir = process.cwd(); // доступен ли yaga.config.yaml const yagaConfigYamlPath = path.join( projectDir, 'yaga.config.yaml'); if (!fs.existsSync(yagaConfigYamlPath)) { console.log(toolName + '\x1b[31mПродолжение невозможно: отсутсвует yaga.config.yaml - ' + yagaConfigYamlPath + '\x1b[0m'); process.exit(1); } // читаем yaga.config.yaml let yagaConfigYaml = ''; try { yagaConfigYaml = fs.readFileSync( yagaConfigYamlPath , 'utf8'); } catch (err) { console.log(toolName + '\x1b[31mПродолжение невозможно: не могу прочитать yaga.config.yaml - ' + yagaConfigYamlPath + '\x1b[0m'); process.exit(1); } // парсим yaga.config.yaml let yagaConfig = ''; try { yagaConfig = yaml.parse(yagaConfigYaml); } catch (err) { console.log(toolName + '\x1b[31mПродолжение невозможно: не могу разобрать yaga.config.yaml - ' + yagaConfigYamlPath + '\x1b[0m'); console.log(err); process.exit(1); } // проверяем yagaConfig.target_dir if(!yagaConfig.target_dir || typeof yagaConfig.target_dir != 'string') { console.log(toolName + '\x1b[31mПродолжение невозможно: неверно указана целевая директория yagaConfig.target_dir - ' + yagaConfig.target_dir + '\x1b[0m'); process.exit(1); } const targetDirPath = path.join(projectDir, yagaConfig.target_dir); // проверяем yagaConfig.target_dir if(!fs.existsSync(targetDirPath)) { console.log(toolName + '\x1b[31mПродолжение невозможно: отсутсвует целевая директория yagaConfig.target_dir - ' + targetDirPath + '\x1b[0m'); process.exit(1); } //*** конец общий код для всех утилит // проверка секции define в конфиге if (!yagaConfig.define) { console.error(`${toolName}\x1b[31mПродолжение невозможно: в ${yagaConfigYamlPath} отсутствует ключ define\x1b[0m`); process.exit(1); } if (!yagaConfig.define.filter || !Array.isArray(yagaConfig.define.filter)) { console.error(`${toolName}\x1b[31mПродолжение невозможно: в ${yagaConfigYamlPath} некорректно настроен ключ define.filter\x1b[0m`); process.exit(1); } if (!yagaConfig.define.ignore || !Array.isArray(yagaConfig.define.ignore)) { console.error(`${toolName}\x1b[31mПродолжение невозможно: в ${yagaConfigYamlPath} некорректно настроен ключ define.ignore\x1b[0m`); process.exit(1); } // глобальные макросы (из конфига) let globalMacros = new Map(); if (yagaConfig.define.global) { if (typeof yagaConfig.define.global === 'object' && !Array.isArray(yagaConfig.define.global)) { for (const [key, val] of Object.entries(yagaConfig.define.global)) { globalMacros.set(key, val); } } } // сканирование файлов const srcFiles = []; function scanDir(dir) { fs.readdirSync(dir).forEach(file => { const absPath = path.join(dir, file); if (fs.statSync(absPath).isDirectory()) return scanDir(absPath); else { let cndf; for(let ref of yagaConfig.define.filter) { try { cndf = (new RegExp(ref)).test(absPath); if(cndf) break; } catch (err) { console.log(); console.log(toolName + '\x1b[31mПродолжение невозможно: в ' + yagaConfigYamlPath + ' неккоректно настроен ключ define.filter\x1b[0m'); process.exit(1); } } if(cndf) { let cndi; for(let rei of yagaConfig.define.ignore) { try { cndi = (new RegExp(rei)).test(absPath); if(cndi) break; } catch (err) { console.log(); console.log(toolName + '\x1b[31mПродолжение невозможно: в ' + yagaConfigYamlPath + ' неккоректно настроен ключ define.ignore\x1b[0m'); process.exit(1); } } if(!cndi) srcFiles.push(absPath); } } }); } scanDir(targetDirPath); if (srcFiles.length === 0) { console.log(); console.log(`${toolName}Нет файлов для обработки.`); process.exit(0); } function evaluateCondition(expr, macros) { expr = expr.trim(); if (expr === '') return true; expr = expr.replace(/defined\s*\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)/g, (_, name) => { return macros.has(name) ? 'true' : 'false'; }); expr = expr.replace(/!defined\s*\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)/g, (_, name) => { return macros.has(name) ? 'false' : 'true'; }); const macroNames = Array.from(macros.keys()).sort((a, b) => b.length - a.length); for (const name of macroNames) { const re = new RegExp(`\\b${name}\\b`, 'g'); let value = macros.get(name); if (typeof value === 'string') value = `'${value.replace(/'/g, "\\'")}'`; else if (typeof value === 'object') value = 'null'; expr = expr.replace(re, value); } try { const fn = new Function('return (' + expr + ')'); // return !!fn(); } catch (err) { console.log(); console.log(`${toolName}\x1b[31mОшибка вычисления условия: ${expr}\x1b[0m`); process.exit(1); } } function processFile(filePath, globalMacrosCopy) { let srcText; try { srcText = fs.readFileSync(filePath, 'utf8'); } catch (err) { console.log(); console.log(`${toolName}\x1b[31mНе могу прочитать ${filePath}\x1b[0m`); process.exit(1); } const macros = new Map(globalMacrosCopy); let modified = false; // Используем формат /**@ ... */ const directiveRe = /#?\/\*\*@(define|if|elif|else|endif)\s*([\s\S]*?)\s*\*\//g; let lastIndex = 0; const outputParts = []; const stack_if = [{ active: true, cond: false }]; let activePart = true; let match; while ((match = directiveRe.exec(srcText)) !== null) { const fullMatch = match[0]; const directiveName = match[1]; let directiveArgs = match[2] ? match[2].trim() : ''; if (activePart) { outputParts.push(srcText.slice(lastIndex, match.index)); } lastIndex = match.index + fullMatch.length; switch (directiveName) { case 'define': if (activePart && directiveArgs) { const spaceIdx = directiveArgs.search(/\s/); let name, value; if (spaceIdx === -1) { name = directiveArgs; value = true; } else { name = directiveArgs.slice(0, spaceIdx); let rawValue = directiveArgs.slice(spaceIdx + 1).trim(); if (rawValue === 'true') value = true; else if (rawValue === 'false') value = false; else if (!isNaN(rawValue) && rawValue !== '') value = Number(rawValue); else value = rawValue; } macros.set(name, value); modified = true; } break; case 'if': { const cond = evaluateCondition(directiveArgs, macros); const newActivePart = activePart && cond; stack_if.push({ active: newActivePart, cond: cond }); activePart = newActivePart; modified = true; } break; case 'elif': { if (stack_if.length <= 1) { console.log(); console.log(`${toolName}\x1b[31m@elif без @if в ${filePath}\x1b[0m`); process.exit(1); } const top = stack_if[stack_if.length - 1]; if (!top.cond) { const cond = evaluateCondition(directiveArgs, macros); if (cond) { top.cond = true; activePart = true; } else { activePart = false; } } else { activePart = false; } modified = true; } break; case 'else': { if (stack_if.length <= 1) { console.log(); console.log(`${toolName}\x1b[31m@else без @if в ${filePath}\x1b[0m`); process.exit(1); } const top = stack_if[stack_if.length - 1]; if (!top.cond) { top.cond = true; activePart = true; } else { activePart = false; } modified = true; } break; case 'endif': { if (stack_if.length <= 1) { console.log(); console.log(`${toolName}\x1b[31m@endif без @if в ${filePath}\x1b[0m`); process.exit(1); } stack_if.pop(); activePart = stack_if[stack_if.length - 1].active; modified = true; } break; } process.stdout.write('\b' + indicator()); } if (activePart) { outputParts.push(srcText.slice(lastIndex)); } if (modified) { const outputText = outputParts.join(''); // try { fs.writeFileSync(filePath, outputText, 'utf8'); } catch (err) { console.log(); console.log(`${toolName}\x1b[31mНе могу записать ${filePath}\x1b[0m`); process.exit(1); } } return modified; } process.stdout.write(`${toolName}Выполняю обработку условий сборки в ./${yagaConfig.target_dir}: `); let anyModified = false; for (const file of srcFiles) { try { const modified = processFile(file, globalMacros); if (modified) anyModified = true; } catch (err) { console.error(); console.error(`${toolName}\x1b[31mОбработка прервана из-за ошибки в файле ${file}\x1b[0m`); process.exit(1); } } process.stdout.write('\b'); process.stdout.write('\x1b[32mготово\x1b[0m'); console.log();