mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
- Added a new script (dev.mjs) to start the application in development mode with hot reloading using Vite. - The script includes functionality for installing Playwright browsers, resolving port configurations, and launching either a web or desktop application. - Removed the old init.mjs script, which was previously responsible for launching the application. - Updated package.json to reference the new dev.mjs script for the development command. - Introduced a shared utilities module (launcher-utils.mjs) for common functionalities used in both development and production scripts.
265 lines
7.3 KiB
JavaScript
Executable File
265 lines
7.3 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Automaker - Production Mode Launch Script
|
|
*
|
|
* This script runs the application in production mode (no Vite dev server).
|
|
* It builds everything if needed, then serves static files via vite preview.
|
|
*
|
|
* Key differences from dev.mjs:
|
|
* - Uses pre-built static files instead of Vite dev server (faster startup)
|
|
* - No HMR or hot reloading
|
|
* - Server runs from compiled dist/ directory
|
|
* - Uses "vite preview" to serve static UI files
|
|
*
|
|
* Usage: npm run start
|
|
*/
|
|
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
import {
|
|
createRestrictedFs,
|
|
log,
|
|
runNpm,
|
|
runNpmAndWait,
|
|
runNpx,
|
|
printHeader,
|
|
printModeMenu,
|
|
resolvePortConfiguration,
|
|
createCleanupHandler,
|
|
setupSignalHandlers,
|
|
startServerAndWait,
|
|
ensureDependencies,
|
|
prompt,
|
|
killProcessTree,
|
|
sleep,
|
|
} from './scripts/launcher-utils.mjs';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
// Create restricted fs for this script's directory
|
|
const fs = createRestrictedFs(__dirname, 'start.mjs');
|
|
|
|
// Track background processes for cleanup
|
|
const processes = {
|
|
server: null,
|
|
web: null,
|
|
electron: null,
|
|
};
|
|
|
|
/**
|
|
* Check if production builds exist
|
|
* @returns {{server: boolean, ui: boolean, electron: boolean}}
|
|
*/
|
|
function checkBuilds() {
|
|
const serverDist = path.join(__dirname, 'apps', 'server', 'dist');
|
|
const uiDist = path.join(__dirname, 'apps', 'ui', 'dist');
|
|
const electronDist = path.join(__dirname, 'apps', 'ui', 'dist-electron', 'main.js');
|
|
|
|
return {
|
|
server: fs.existsSync(serverDist),
|
|
ui: fs.existsSync(uiDist),
|
|
electron: fs.existsSync(electronDist),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Build all production artifacts if needed
|
|
*/
|
|
async function ensureProductionBuilds() {
|
|
// Always build shared packages first to ensure they're up to date
|
|
log('Building shared packages...', 'blue');
|
|
try {
|
|
await runNpmAndWait(['run', 'build:packages'], { stdio: 'inherit' }, __dirname);
|
|
log('✓ Shared packages built', 'green');
|
|
} catch (error) {
|
|
log(`Failed to build shared packages: ${error.message}`, 'red');
|
|
process.exit(1);
|
|
}
|
|
|
|
// Always rebuild server to ensure it's in sync with packages
|
|
log('Building server...', 'blue');
|
|
try {
|
|
await runNpmAndWait(
|
|
['run', 'build'],
|
|
{ stdio: 'inherit' },
|
|
path.join(__dirname, 'apps', 'server')
|
|
);
|
|
log('✓ Server built', 'green');
|
|
} catch (error) {
|
|
log(`Failed to build server: ${error.message}`, 'red');
|
|
process.exit(1);
|
|
}
|
|
|
|
// Check if UI/Electron builds exist (these are slower, so only build if missing)
|
|
const builds = checkBuilds();
|
|
|
|
if (!builds.ui || !builds.electron) {
|
|
log('UI/Electron builds not found. Building...', 'yellow');
|
|
console.log('');
|
|
|
|
try {
|
|
log('Building UI...', 'blue');
|
|
await runNpmAndWait(['run', 'build'], { stdio: 'inherit' }, __dirname);
|
|
log('✓ Build complete!', 'green');
|
|
console.log('');
|
|
} catch (error) {
|
|
log(`Build failed: ${error.message}`, 'red');
|
|
process.exit(1);
|
|
}
|
|
} else {
|
|
log('✓ UI builds found', 'green');
|
|
console.log('');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Main function
|
|
*/
|
|
async function main() {
|
|
// Change to script directory
|
|
process.chdir(__dirname);
|
|
|
|
printHeader('Automaker Production Mode');
|
|
|
|
// Ensure dependencies are installed
|
|
await ensureDependencies(fs, __dirname);
|
|
|
|
// Build production artifacts if needed
|
|
await ensureProductionBuilds();
|
|
|
|
// Resolve port configuration (check/kill/change ports)
|
|
const { webPort, serverPort, corsOriginEnv } = await resolvePortConfiguration();
|
|
|
|
// Show mode selection menu
|
|
printModeMenu();
|
|
|
|
// Setup cleanup handlers
|
|
const cleanup = createCleanupHandler(processes);
|
|
setupSignalHandlers(cleanup);
|
|
|
|
// Prompt for choice
|
|
while (true) {
|
|
const choice = await prompt('Enter your choice (1 or 2): ');
|
|
|
|
if (choice === '1') {
|
|
console.log('');
|
|
log('Launching Web Application (Production Mode)...', 'blue');
|
|
|
|
// Start the backend server in PRODUCTION mode
|
|
// Uses "npm run start" in apps/server which runs the compiled dist/
|
|
// NOT the Vite dev server (no HMR, faster startup)
|
|
processes.server = await startServerAndWait({
|
|
serverPort,
|
|
corsOriginEnv,
|
|
npmArgs: ['run', 'start'],
|
|
cwd: path.join(__dirname, 'apps', 'server'),
|
|
fs,
|
|
baseDir: __dirname,
|
|
});
|
|
|
|
if (!processes.server) {
|
|
cleanup();
|
|
process.exit(1);
|
|
}
|
|
|
|
log(`Starting web server...`, 'blue');
|
|
|
|
// Start vite preview to serve pre-built static files
|
|
// This is NOT Vite dev server - it just serves the dist/ folder
|
|
// No HMR, no compilation, just static file serving
|
|
processes.web = runNpx(
|
|
['vite', 'preview', '--port', String(webPort)],
|
|
{
|
|
stdio: 'inherit',
|
|
env: {
|
|
VITE_SERVER_URL: `http://localhost:${serverPort}`,
|
|
},
|
|
},
|
|
path.join(__dirname, 'apps', 'ui')
|
|
);
|
|
|
|
log(`The application is available at: http://localhost:${webPort}`, 'green');
|
|
console.log('');
|
|
|
|
await new Promise((resolve) => {
|
|
processes.web.on('close', resolve);
|
|
});
|
|
|
|
break;
|
|
} else if (choice === '2') {
|
|
console.log('');
|
|
log('Launching Desktop Application (Production Mode)...', 'blue');
|
|
log('(Electron will start its own backend server)', 'yellow');
|
|
console.log('');
|
|
|
|
// Run electron directly with the built main.js
|
|
const electronMainPath = path.join(__dirname, 'apps', 'ui', 'dist-electron', 'main.js');
|
|
|
|
if (!fs.existsSync(electronMainPath)) {
|
|
log('Error: Electron main process not built. Run build first.', 'red');
|
|
process.exit(1);
|
|
}
|
|
|
|
// Start vite preview to serve built static files for electron
|
|
// (Electron in non-packaged mode needs a server to load from)
|
|
log('Starting static file server...', 'blue');
|
|
processes.web = runNpx(
|
|
['vite', 'preview', '--port', String(webPort)],
|
|
{
|
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
env: {
|
|
VITE_SERVER_URL: `http://localhost:${serverPort}`,
|
|
},
|
|
},
|
|
path.join(__dirname, 'apps', 'ui')
|
|
);
|
|
|
|
// Wait for vite preview to start
|
|
await sleep(2000);
|
|
|
|
// Use electron from node_modules with NODE_ENV=production
|
|
// This ensures electron loads from the preview server, not Vite dev
|
|
processes.electron = runNpx(
|
|
['electron', electronMainPath],
|
|
{
|
|
stdio: 'inherit',
|
|
env: {
|
|
TEST_PORT: String(webPort),
|
|
PORT: String(serverPort),
|
|
VITE_DEV_SERVER_URL: `http://localhost:${webPort}`,
|
|
VITE_SERVER_URL: `http://localhost:${serverPort}`,
|
|
CORS_ORIGIN: corsOriginEnv,
|
|
NODE_ENV: 'production',
|
|
},
|
|
},
|
|
path.join(__dirname, 'apps', 'ui')
|
|
);
|
|
|
|
await new Promise((resolve) => {
|
|
processes.electron.on('close', () => {
|
|
// Also kill vite preview when electron closes
|
|
if (processes.web && !processes.web.killed && processes.web.pid) {
|
|
killProcessTree(processes.web.pid);
|
|
}
|
|
resolve();
|
|
});
|
|
});
|
|
|
|
break;
|
|
} else {
|
|
log('Invalid choice. Please enter 1 or 2.', 'red');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run main function
|
|
main().catch((err) => {
|
|
console.error(err);
|
|
const cleanup = createCleanupHandler(processes);
|
|
cleanup();
|
|
process.exit(1);
|
|
});
|