Files
Twitch-VOD-Manager/scripts/dev.mjs
T
Sucukdeluxe 9f1b052afd Release Twitch VOD Manager 1.0.18
Harden update, system-check, queue, cutter, streamer and shutdown state transitions.

Add multi-user installer recovery, secret-safe config migration, provider fallback handling, managed-tool validation and real media export coverage.

Refresh the English public documentation, release notes and 1.0.18 product screenshot.
2026-08-13 22:59:15 +02:00

121 lines
4.3 KiB
JavaScript

import { spawn } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { pathToFileURL } from 'node:url';
import { readFileSync, watch } from 'node:fs';
import { dirname, resolve } from 'node:path';
const scriptPath = fileURLToPath(import.meta.url);
const rootDirectory = resolve(dirname(scriptPath), '..');
const developmentAppVersion = JSON.parse(readFileSync(resolve(rootDirectory, 'package.json'), 'utf8')).version;
if (typeof developmentAppVersion !== 'string' || developmentAppVersion.trim().length === 0) {
throw new Error('package.json version must be a non-empty string');
}
const typescriptCli = resolve(rootDirectory, 'node_modules', 'typescript', 'bin', 'tsc');
const electronSourceExecutable = process.platform === 'win32'
? resolve(rootDirectory, 'node_modules', 'electron', 'dist', 'electron.exe')
: resolve(rootDirectory, 'node_modules', '.bin', 'electron');
let electronExecutable = electronSourceExecutable;
const outputDirectory = resolve(rootDirectory, 'dist');
const developmentProgramData = resolve(rootDirectory, '.dev-program-data');
const developmentUserData = resolve(rootDirectory, '.dev-user-data');
const developmentRelaunchCommand = `"${process.execPath}" "${scriptPath}" --once`;
const runOnce = process.argv.includes('--once');
let electronProcess;
let restarting = false;
let restartTimer;
let compiler;
let outputWatcher;
function run(command, args, options = {}) {
return spawn(command, args, { cwd: rootDirectory, stdio: 'inherit', ...options });
}
function waitForExit(child) {
return new Promise((resolveExit, reject) => {
child.once('error', reject);
child.once('exit', (code) => resolveExit(code ?? 1));
});
}
function startElectron() {
electronProcess = run(electronExecutable, [`--user-data-dir=${developmentUserData}`, '.'], {
env: {
...process.env,
PROGRAMDATA: developmentProgramData,
TWITCH_VOD_MANAGER_DEV: '1',
TWITCH_VOD_MANAGER_RELAUNCH_COMMAND: developmentRelaunchCommand,
},
});
electronProcess.once('exit', () => {
electronProcess = undefined;
});
return electronProcess;
}
function restartElectron() {
if (restarting) return;
restarting = true;
if (electronProcess) {
electronProcess.once('exit', () => {
restarting = false;
startElectron();
});
electronProcess.kill();
return;
}
restarting = false;
startElectron();
}
function isElectronRestartTarget(fileName) {
const baseName = fileName.replaceAll('\\', '/').split('/').at(-1) ?? '';
if (baseName === 'renderer.js') return false;
if (!baseName.startsWith('renderer') || !baseName.endsWith('.js')) return true;
const suffix = baseName.slice(8);
return suffix.length <= 4 || (suffix[0] !== '-' && suffix[0] !== '.');
}
function scheduleRestart(fileName) {
if (!fileName || !isElectronRestartTarget(fileName.toString())) return;
clearTimeout(restartTimer);
restartTimer = setTimeout(restartElectron, 200);
}
function stop(child) {
if (child && !child.killed) child.kill();
}
const initialCompile = run(process.execPath, [typescriptCli]);
const initialExitCode = await waitForExit(initialCompile);
if (initialExitCode !== 0) process.exit(initialExitCode);
if (process.platform === 'win32') {
const helperPath = pathToFileURL(resolve(outputDirectory, 'main', 'dev-executable.js')).href;
const { prepareWindowsDevExecutable } = await import(helperPath);
electronExecutable = await prepareWindowsDevExecutable({
sourcePath: electronSourceExecutable,
destinationPath: resolve(rootDirectory, 'node_modules', 'electron', 'dist', 'Twitch VOD Manager.exe'),
iconPath: resolve(rootDirectory, 'build', 'icon.ico'),
version: developmentAppVersion,
});
}
for (const signal of ['SIGINT', 'SIGTERM']) {
process.once(signal, () => {
outputWatcher?.close();
clearTimeout(restartTimer);
stop(compiler);
stop(electronProcess);
process.exit();
});
}
if (runOnce) {
process.exitCode = await waitForExit(startElectron());
} else {
compiler = run(process.execPath, [typescriptCli, '--watch', '--preserveWatchOutput']);
outputWatcher = watch(outputDirectory, { recursive: true }, (_, fileName) => scheduleRestart(fileName));
startElectron();
}