2026-06-08 02:39:44 +08:00
|
|
|
|
using System.CommandLine;
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
|
|
|
|
|
|
namespace LanMountainDesktop.AirAppDevServer;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// AirApp 开发服务器主程序
|
|
|
|
|
|
/// 提供热重载、实时预览等开发功能
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
class Program
|
|
|
|
|
|
{
|
|
|
|
|
|
static async Task<int> Main(string[] args)
|
|
|
|
|
|
{
|
|
|
|
|
|
var rootCommand = new RootCommand("LanMountainDesktop AirApp 开发服务器");
|
|
|
|
|
|
|
|
|
|
|
|
// 开发模式命令
|
|
|
|
|
|
var devCommand = new Command("dev", "启动开发服务器(支持热重载)");
|
2026-07-11 02:27:18 +09:00
|
|
|
|
var projectPathOption = new Option<string>("--project", "-p")
|
|
|
|
|
|
{
|
|
|
|
|
|
Description = "AirApp 项目路径",
|
|
|
|
|
|
DefaultValueFactory = _ => Directory.GetCurrentDirectory(),
|
|
|
|
|
|
Recursive = true
|
|
|
|
|
|
};
|
|
|
|
|
|
var portOption = new Option<int>("--port")
|
|
|
|
|
|
{
|
|
|
|
|
|
Description = "开发服务器端口",
|
|
|
|
|
|
DefaultValueFactory = _ => 5000
|
|
|
|
|
|
};
|
|
|
|
|
|
var verboseOption = new Option<bool>("--verbose", "-v")
|
|
|
|
|
|
{
|
|
|
|
|
|
Description = "显示详细日志"
|
|
|
|
|
|
};
|
2026-06-08 02:39:44 +08:00
|
|
|
|
|
2026-07-11 02:27:18 +09:00
|
|
|
|
rootCommand.Options.Add(projectPathOption);
|
|
|
|
|
|
devCommand.Options.Add(portOption);
|
|
|
|
|
|
devCommand.Options.Add(verboseOption);
|
2026-06-08 02:39:44 +08:00
|
|
|
|
|
2026-07-11 02:27:18 +09:00
|
|
|
|
devCommand.SetAction(async parseResult =>
|
2026-06-08 02:39:44 +08:00
|
|
|
|
{
|
2026-07-11 02:27:18 +09:00
|
|
|
|
await RunDevServerAsync(
|
|
|
|
|
|
parseResult.GetValue(projectPathOption) ?? Directory.GetCurrentDirectory(),
|
|
|
|
|
|
parseResult.GetValue(portOption),
|
|
|
|
|
|
parseResult.GetValue(verboseOption));
|
|
|
|
|
|
});
|
2026-06-08 02:39:44 +08:00
|
|
|
|
|
|
|
|
|
|
// 预览命令
|
|
|
|
|
|
var previewCommand = new Command("preview", "预览 AirApp(无需安装到宿主)");
|
2026-07-11 02:27:18 +09:00
|
|
|
|
var componentOption = new Option<string?>("--component", "-c")
|
|
|
|
|
|
{
|
|
|
|
|
|
Description = "要预览的组件 ID"
|
|
|
|
|
|
};
|
|
|
|
|
|
var windowOption = new Option<string?>("--window", "-w")
|
|
|
|
|
|
{
|
|
|
|
|
|
Description = "要预览的窗口 ID"
|
|
|
|
|
|
};
|
2026-06-08 02:39:44 +08:00
|
|
|
|
|
2026-07-11 02:27:18 +09:00
|
|
|
|
previewCommand.Options.Add(componentOption);
|
|
|
|
|
|
previewCommand.Options.Add(windowOption);
|
2026-06-08 02:39:44 +08:00
|
|
|
|
|
2026-07-11 02:27:18 +09:00
|
|
|
|
previewCommand.SetAction(async parseResult =>
|
2026-06-08 02:39:44 +08:00
|
|
|
|
{
|
2026-07-11 02:27:18 +09:00
|
|
|
|
await RunPreviewAsync(
|
|
|
|
|
|
parseResult.GetValue(projectPathOption) ?? Directory.GetCurrentDirectory(),
|
|
|
|
|
|
parseResult.GetValue(componentOption),
|
|
|
|
|
|
parseResult.GetValue(windowOption));
|
|
|
|
|
|
});
|
2026-06-08 02:39:44 +08:00
|
|
|
|
|
|
|
|
|
|
// 打包命令
|
|
|
|
|
|
var packageCommand = new Command("package", "打包 AirApp 为 .laapp 文件");
|
2026-07-11 02:27:18 +09:00
|
|
|
|
var outputOption = new Option<string?>("--output", "-o")
|
|
|
|
|
|
{
|
|
|
|
|
|
Description = "输出路径"
|
|
|
|
|
|
};
|
2026-06-08 02:39:44 +08:00
|
|
|
|
|
2026-07-11 02:27:18 +09:00
|
|
|
|
packageCommand.Options.Add(outputOption);
|
2026-06-08 02:39:44 +08:00
|
|
|
|
|
2026-07-11 02:27:18 +09:00
|
|
|
|
packageCommand.SetAction(async parseResult =>
|
2026-06-08 02:39:44 +08:00
|
|
|
|
{
|
2026-07-11 02:27:18 +09:00
|
|
|
|
await PackageAirAppAsync(
|
|
|
|
|
|
parseResult.GetValue(projectPathOption) ?? Directory.GetCurrentDirectory(),
|
|
|
|
|
|
parseResult.GetValue(outputOption));
|
|
|
|
|
|
});
|
2026-06-08 02:39:44 +08:00
|
|
|
|
|
2026-07-11 02:27:18 +09:00
|
|
|
|
rootCommand.Subcommands.Add(devCommand);
|
|
|
|
|
|
rootCommand.Subcommands.Add(previewCommand);
|
|
|
|
|
|
rootCommand.Subcommands.Add(packageCommand);
|
2026-06-08 02:39:44 +08:00
|
|
|
|
|
2026-07-11 02:27:18 +09:00
|
|
|
|
return await rootCommand.Parse(args).InvokeAsync();
|
2026-06-08 02:39:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static async Task RunDevServerAsync(string projectPath, int port, bool verbose)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("🚀 启动 AirApp 开发服务器...");
|
|
|
|
|
|
Console.WriteLine($"📁 项目路径: {projectPath}");
|
|
|
|
|
|
Console.WriteLine($"🔌 端口: {port}");
|
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
|
|
|
|
|
|
|
var server = new AirAppDevServer(projectPath, port, verbose);
|
|
|
|
|
|
await server.StartAsync();
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
|
Console.WriteLine("✅ 开发服务器已启动");
|
|
|
|
|
|
Console.WriteLine($"🌐 预览地址: http://localhost:{port}");
|
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
|
Console.WriteLine("按 Ctrl+C 停止服务器...");
|
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
|
|
|
|
|
|
|
// 等待取消信号
|
|
|
|
|
|
var cts = new CancellationTokenSource();
|
|
|
|
|
|
Console.CancelKeyPress += (sender, e) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
e.Cancel = true;
|
|
|
|
|
|
cts.Cancel();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
await Task.Delay(Timeout.Infinite, cts.Token);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (TaskCanceledException)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
|
Console.WriteLine("🛑 正在停止服务器...");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await server.StopAsync();
|
|
|
|
|
|
Console.WriteLine("✅ 服务器已停止");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static async Task RunPreviewAsync(string projectPath, string? component, string? window)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("👁️ 启动 AirApp 预览...");
|
|
|
|
|
|
Console.WriteLine($"📁 项目路径: {projectPath}");
|
|
|
|
|
|
|
|
|
|
|
|
var previewer = new AirAppPreviewer(projectPath);
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(component))
|
|
|
|
|
|
{
|
|
|
|
|
|
await previewer.PreviewComponentAsync(component);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!string.IsNullOrEmpty(window))
|
|
|
|
|
|
{
|
|
|
|
|
|
await previewer.PreviewWindowAsync(window);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
await previewer.PreviewAllAsync();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static async Task PackageAirAppAsync(string projectPath, string? output)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("📦 打包 AirApp...");
|
|
|
|
|
|
Console.WriteLine($"📁 项目路径: {projectPath}");
|
|
|
|
|
|
|
|
|
|
|
|
var packager = new AirAppPackager(projectPath);
|
|
|
|
|
|
var outputPath = await packager.PackageAsync(output);
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine();
|
|
|
|
|
|
Console.WriteLine($"✅ 打包完成: {outputPath}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|