Spaces:
Running
Running
File size: 8,062 Bytes
1ae2e8e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
using System.Reflection;
using LibGit2Sharp;
using CommandLine;
using Spectre.Console;
using DotNet = Microsoft.DotNet.Cli.Utils;
namespace RobloxCS.CLI
{
public static class Program
{
private const string _exampleProjectRepo = "https://github.com/roblox-csharp/example-project.git";
private static readonly Assembly _compilerAssembly = Assembly.Load("RobloxCS");
private static readonly System.Version _version = _compilerAssembly.GetName().Version!;
public class Options
{
[Option('v', "version", Required = false, HelpText = "Return the compiler version.")]
public bool Version { get; set; }
[Option('w', "watch", Required = false, HelpText = "Watches the directory and re-compiles when changes are detected.")]
public bool WatchMode { get; set; }
[Option("init", Required = false, HelpText = "Initialize a new roblox-cs project.")]
public bool Init { get; set; }
[Value(0, Required = false, HelpText = "Project directory path", MetaName = "Path")]
public string? Path { get; set; }
}
public static void Main(string[] args)
{
Parser.Default.ParseArguments<Options>(args)
.WithParsed(options =>
{
var path = options.Path ?? ".";
if (options.Version)
{
Console.WriteLine($"roblox-cs {_version.Major}.{_version.Minor}.{_version.Build}");
}
else if (options.Init)
{
InitializeProject(path);
}
else if (options.WatchMode)
{
StartWatchMode(path);
}
else
{
new Transpiler(path).Transpile();
}
});
}
private static void StartWatchMode(string path)
{
var transpiler = new Transpiler(path);
var watcher = new FileSystemWatcher
{
Path = path,
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.Size,
Filter = "*.*",
IncludeSubdirectories = true
};
watcher.Changed += (sender, e) => OnFileChanged(path, e);
watcher.Created += (sender, e) => OnFileChanged(path, e);
watcher.Renamed += (sender, e) => OnFileChanged(path, e);
watcher.Deleted += (sender, e) => OnFileChanged(path, e);
watcher.EnableRaisingEvents = true;
AnsiConsole.MarkupLine("[yellow]Watching for file changes. Press \"enter\" to exit.[/]");
Console.WriteLine();
Console.ReadLine();
}
private static void OnFileChanged(string path, FileSystemEventArgs e)
{
HashSet<WatcherChangeTypes> validChangeTypes = [WatcherChangeTypes.Changed, WatcherChangeTypes.Created, WatcherChangeTypes.Renamed];
if (!validChangeTypes.Contains(e.ChangeType)) return;
var filePath = Utility.FixPathSep(e.FullPath);
var extension = Path.GetExtension(filePath);
var transpiler = new Transpiler(path);
var isValidLuaFile = extension == ".lua"
&& !filePath.EndsWith("include/RuntimeLib.lua")
&& !filePath.Contains($"/{transpiler.Config.OutputFolder}/");
if (extension == ".cs" || isValidLuaFile || extension == ".yml")
{
AnsiConsole.MarkupLine($"[yellow]{(e.ChangeType == WatcherChangeTypes.Renamed ? "Changed" : e.ChangeType)}: {filePath}[/]");
transpiler.Transpile();
}
}
private static void InitializeProject(string path)
{
var projectName = Path.GetDirectoryName(path) == "" ? Path.GetFileName(path) : Path.GetDirectoryName(path) ?? "Example";
try
{
Repository.Clone(_exampleProjectRepo, path);
AnsiConsole.MarkupLine($"[cyan]Repository cloned to {Path.GetFullPath(path)}[/]");
}
catch (Exception ex)
{
AnsiConsole.MarkupLine($"[red]Failed to clone example project repository: {ex.Message}[/]");
Environment.Exit(1);
}
DeleteDirectoryManual(Path.Combine(path, ".git"));
DotNet.Command.Create("dotnet", ["restore", path]).Execute();
AnsiConsole.MarkupLine("[cyan]Successfully restored .NET project.[/]");
if (projectName != "Example")
{
var projectFolder = path;
var projectSourceFolder = Path.Combine(projectFolder, projectName, "src");
var projectFile = Path.Combine(projectSourceFolder, $"{projectName}.csproj");
var rojoManifestFile = Path.Combine(projectFolder, projectName, "default.project.json");
AnsiConsole.MarkupLine("[yellow]Renaming project...[/]");
File.Delete(Path.Combine(path, "Example.sln"));
DotNet.Command.Create("dotnet", ["new", "sln", "-n", projectName, "-o", path]).Execute();
Directory.Move(Path.Combine(path, "Example"), Path.Combine(projectFolder, projectName));
File.Move(Path.Combine(projectSourceFolder, "Example.csproj"), projectFile);
// Dynamicallty replace <Title>...</Title> inside of the csproj file. This sucks, and we must edit the solution to not lose the reference to it.
var lines = File.ReadAllText(projectFile);
var start = lines.IndexOf("<Title>", StringComparison.InvariantCulture) + "<Title>".Length;
var end = lines.IndexOf("</Title>", StringComparison.InvariantCulture);
lines = lines.Remove(start, end - start);
lines = lines.Insert(start, projectName);
File.WriteAllText(projectFile, lines);
DotNet.Command.Create("dotnet", ["sln", Path.Combine(path, $"{projectName}.sln"), "add", projectFile]).Execute();
AnsiConsole.MarkupLine("[cyan]Successfully renamed project.[/]");
AnsiConsole.MarkupLine("[yellow]Modifying rojo manifest...[/]");
var rojoManifest = File.ReadAllText(rojoManifestFile);
// Match for the name JSON key and its end and replace the name of it, horrible, but works.
var idxOfStart = rojoManifest.IndexOf("\"name\": \"", StringComparison.InvariantCulture) + "\"name\": \"".Length;
var idxOfEnd = rojoManifest.IndexOf("\",", idxOfStart, StringComparison.InvariantCulture);
rojoManifest = rojoManifest.Remove(idxOfStart, idxOfEnd - idxOfStart);
rojoManifest = rojoManifest.Insert(idxOfStart, projectName);
File.WriteAllText(rojoManifestFile, rojoManifest);
}
Console.WriteLine($"Configuration:");
var initRepo = AnsiConsole.Confirm("\t[yellow]Do you want to initialize a Git repository?[/]", true);
if (initRepo)
{
Repository.Init(path);
AnsiConsole.MarkupLine("[cyan]Successfully initialized Git repository.[/]");
}
}
private static void DeleteDirectoryManual(string path)
{
if (Directory.Exists(path))
{
foreach (string file in Directory.GetFiles(path))
{
File.SetAttributes(file, FileAttributes.Normal); // Ensure file is not read-only
File.Delete(file);
}
foreach (string subdir in Directory.GetDirectories(path))
{
DeleteDirectoryManual(subdir);
}
Directory.Delete(path, recursive: true);
}
}
}
} |