/
ivanby
/
Compote
Обзор
Документация
Войти
/
ivanby
/
Compote
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
1
CI/CD
Аналитика
Безопасность
master
yaga/exec.js
232 строки
9 KB
Иван Быков
Initial commit
19 июн 2026, 08:50
19 июн 2026, 08:50
b4391e5
Код
Авторство
О чём код?
// выполнение произвольных скриптов, // скрипт-функция должен вернуть текст в process.write, и подстановка -- директива [#]/**@exec[!] exec/скрипт.js*/ // необязательный "!" -- флаг некэшировать, необязательный "#" -- для yaml, python, bash и т.п., // Например, /**@exec exec/time.js DD.MM.YYYY*/ -- вставит в текст текущую дату, // а /**@exec exec/version.js*/ -- текущую версию сборки // exec удобно применять для генерации сложного кода html, svg, типа статичных формул и графиков. // может заменить yaga/insert.js, если вставки нужны с условиями. const toolName = 'exec -> '; //*** начало общий код для всех утилит 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); } //*** конец общий код для всех утилит // собственно код exec.js const execSync = require('node:child_process').execSync; // проверяем yagaConfig.exec if( !yagaConfig.exec || !yagaConfig.exec.filter || !Array.isArray(yagaConfig.exec.filter) || !yagaConfig.exec.ignore || !Array.isArray(yagaConfig.exec.ignore) ) { console.log(toolName + '\x1b[31mПродолжение невозможно: в ' + yagaConfigYamlPath + ' неккоректно настроен ключ exec\x1b[0m'); process.exit(1); } let 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.exec.filter) { try { cndf = (new RegExp(ref)).test(absPath); if(cndf) break; } catch (err) { console.log(toolName + '\x1b[31mПродолжение невозможно: в ' + yagaConfigYamlPath + ' неккоректно настроен ключ exec.filter\x1b[0m'); process.exit(1); } } if(cndf) { let cndi; for(let rei of yagaConfig.exec.ignore) { try { cndi = (new RegExp(rei)).test(absPath); if(cndi) break; } catch (err) { console.log(toolName + '\x1b[31mПродолжение невозможно: в ' + yagaConfigYamlPath + ' неккоректно настроен ключ exec.ignore\x1b[0m'); process.exit(1); } } if(!cndi) srcFiles.push(absPath); } } }); } scanDir(targetDirPath); //console.log(srcFiles); let execCache = []; const execCmdRe = /#?\/\*\*@exec(!?)\s+([^]+?)\s*\*\//gmi; let execNoti = toolName + 'Выполняю скрипты-вставки в ./' + yagaConfig.target_dir + ': '; process.stdout.write(execNoti); const handFiles = ()=>{ for( let srcFile of srcFiles) { let srcText = ''; try { srcText = fs.readFileSync( srcFile , 'utf8'); } catch (err) { console.log(); console.log(toolName + '\x1b[31mПродолжение невозможно: не могу прочитать ' + srcFile + '\x1b[0m'); console.log(err); process.exit(1); } let modi = false; srcText = srcText.replace(execCmdRe, function(match, p1, p2) { modi = true; // рзбираем p2 if(p2[0] === '.' && p2[1] === '/' ) // это путь относительно текущего srcFile p2 = path.join(path.dirname(srcFile), p2.slice(2)); else if(p2[0] === '.' && p2[1] === '.' && p2[2] === '/' ) // это путь относительно родителя текущего srcFile p2 = path.join(path.dirname(path.dirname(srcFile)), p2.slice(3)); else if(p2[0] === '/' && p2[1] === '/') // это путь относительно projectDir p2 = path.join(projectDir, p2.slice(2)); else if(p2[0] !== '/') // это путь относительно targetDirPath p2 = path.join(targetDirPath, p2); // дефолт -- это абсолютный путь -- '/' if(srcFile === p2) { console.log(); console.log(toolName + '\x1b[31mПродолжение невозможно: попытка перекресного выполнения в ' + p2 + '\x1b[0m'); console.log(toolName + '\x1b[31mиз ' + srcFile + '\x1b[0m'); process.exit(1); } let findItmC = execCache.find(itm=> itm.p === p2); if(findItmC && findItmC.c) return findItmC.v; let val = ''; try { val = execSync('node ' + p2); } catch (err) { console.log(); console.log(toolName + '\x1b[31mПродолжение невозможно: не могу выполнить ' + p2 + '\x1b[0m'); console.log(toolName + '\x1b[31mиз ' + srcFile + '\x1b[0m'); process.exit(1); } if(!findItmC) execCache.push({c: p1 !== '!', p: p2, v: val}); process.stdout.write('\b'); process.stdout.write(indicator()); return val; }); if(modi) try { fs.writeFileSync( srcFile , srcText, 'utf8'); handFiles(); break; } catch (err) { console.log(); console.log(toolName + '\x1b[31mПродолжение невозможно: не могу записать ' + srcFile + '\x1b[0m'); console.log(err); process.exit(1); } } }; handFiles(); // удаляем отработанные скрипты // только те, что находятся в targetDirPath execCache.forEach(itm=>{ if(!itm.p.startsWith(targetDirPath)) return; let file = path.join(targetDirPath, itm.p.split(/\s+/)[0]); try { if(fs.existsSync(file)) fs.unlinkSync(file); } catch (err) { console.log(); console.log(toolName + '\x1b[31mПродолжение невозможно: не могу удалить ' + file + '\x1b[0m'); console.log(err); process.exit(1); } }); process.stdout.write('\b'); process.stdout.write('\x1b[32mготово\x1b[0m'); console.log();