diff --git a/scripts/doctor.mjs b/scripts/doctor.mjs index 2ce4930..02a6b98 100755 --- a/scripts/doctor.mjs +++ b/scripts/doctor.mjs @@ -15,6 +15,7 @@ import fs from 'node:fs'; import path from 'node:path'; import crypto from 'node:crypto'; +import { execFileSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; const HERE = path.dirname(fileURLToPath(import.meta.url)); @@ -47,6 +48,24 @@ const SERVER_CORE = path.join(BACK, 'core'); // «Demo Panel» в нём норма, а не недоделка. Узнаётся по каталогу core/ рядом. const IS_TEMPLATE = fs.existsSync(path.join(PANEL, 'core', 'browser')); +// Ядро попадает в панель одним из двух способов, и проверки у них разные: +// +// subtree — панель лежит в git-репозитории продукта, ядро подтянуто +// `git subtree add` и потому отслеживается git. Обновляется +// `git subtree pull`; update.sh здесь не нужен и вреден — он +// сносит каталог целиком и затёр бы поддерево. +// +// copy — ядро раскладывает update.sh из соседнего ma7engine. Так устроен +// сам шаблон, и так живёт панель вне git. +function coreIsTracked(dir) { + try { + const out = execFileSync('git', ['ls-files', '--', dir], + { cwd: PANEL, stdio: ['ignore', 'pipe', 'ignore'] }); + return out.toString().trim() !== ''; + } catch { return false; } +} +const MODE = !IS_TEMPLATE && coreIsTracked(path.join(FRONT, 'src/core')) ? 'subtree' : 'copy'; + // ─── накопитель результатов ───────────────────────────────────────────────── const errors = [], warns = [], skips = [], oks = []; @@ -71,20 +90,33 @@ if (!fs.existsSync(FRONT)) { } const updateSh = path.join(FRONT, 'scripts/update.sh'); -if (fs.existsSync(updateSh)) ok('update.sh на месте'); +if (MODE === 'subtree') { + if (fs.existsSync(updateSh)) { + err('в subtree-панели остался frontend/scripts/update.sh', + 'он сносит каталог ядра целиком (rm -rf) и затрёт подтянутое поддерево — ' + + 'обновление здесь делается git subtree pull'); + } else ok('ядро приходит через git subtree (update.sh не нужен)'); +} else if (fs.existsSync(updateSh)) ok('update.sh на месте'); else err('нет frontend/scripts/update.sh', 'без него ядро в панели не обновляется'); const frontPkgText = need(path.join(FRONT, 'package.json'), 'фронт'); const frontPkg = frontPkgText ? JSON.parse(frontPkgText) : null; if (frontPkg) { const s = frontPkg.scripts ?? {}; - if (/update\.sh/.test(s.update ?? '')) ok('скрипт update ведёт на scripts/update.sh'); - else err('frontend/package.json: скрипт "update" не зовёт scripts/update.sh'); + if (MODE === 'subtree') { + const stale = ['update', 'predev', 'prebuild'].filter(h => /update/.test(s[h] ?? '')); + if (stale.length) err(`frontend/package.json: остались хуки копирования (${stale.join(', ')})`, + 'ядро приходит поддеревом — эти скрипты его затрут'); + else ok('в package.json нет хуков копирования ядра'); + } else { + if (/update\.sh/.test(s.update ?? '')) ok('скрипт update ведёт на scripts/update.sh'); + else err('frontend/package.json: скрипт "update" не зовёт scripts/update.sh'); - for (const hook of ['predev', 'prebuild']) { - if (/update/.test(s[hook] ?? '')) ok(`${hook} обновляет ядро`); - else err(`frontend/package.json: нет "${hook}": "npm run update"`, - 'без него dev и build идут на устаревшей копии ядра'); + for (const hook of ['predev', 'prebuild']) { + if (/update/.test(s[hook] ?? '')) ok(`${hook} обновляет ядро`); + else err(`frontend/package.json: нет "${hook}": "npm run update"`, + 'без него dev и build идут на устаревшей копии ядра'); + } } } @@ -330,7 +362,7 @@ if (!IS_TEMPLATE) { // ─── вывод ────────────────────────────────────────────────────────────────── const rel = path.relative(process.cwd(), PANEL) || '.'; -console.log(`doctor: ${rel}${IS_TEMPLATE ? ' (шаблон ma7engine)' : ''}\n`); +console.log(`doctor: ${rel}${IS_TEMPLATE ? ' (шаблон ma7engine)' : ` (ядро: ${MODE})`}\n`); for (const m of oks) console.log(` ok ${m}`); for (const m of skips) console.log(` — ${m}`); diff --git a/scripts/new-panel.mjs b/scripts/new-panel.mjs index 47ac599..41683ff 100755 --- a/scripts/new-panel.mjs +++ b/scripts/new-panel.mjs @@ -76,7 +76,15 @@ function edit(relPath, pairs) { fs.writeFileSync(file, text); } -edit('frontend/package.json', [['"ma7engine-example-frontend"', `"${slug}-frontend"`]]); +edit('frontend/package.json', [ + ['"ma7engine-example-frontend"', `"${slug}-frontend"`], + // В панели ядро приезжает через git subtree, а не копированием: хуки + // predev/prebuild и сам update.sh здесь не нужны и вредны — update.sh + // сносит каталог целиком (rm -rf) и затёр бы подтянутое поддерево. + [` "predev": "npm run update",\n`, ''], + [` "update": "sh scripts/update.sh",\n`, ''], + [` "prebuild": "npm run update",\n`, ''], +]); // Заголовок вкладки браузера — не то же, что branding.name в App.tsx: его видно // до того, как React смонтируется, и в закладках. edit('frontend/index.html', [['