summary refs log tree commit diff
path: root/ReferenceClientProxyImplementation/Patches/Implementations/FormatHtmlCssPatch.cs
blob: 877a31a135ea01eaa236587e81871d7db8123d9f (plain) (blame)
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
using System.Diagnostics;
using ArcaneLibs;
using ReferenceClientProxyImplementation.Configuration;

namespace ReferenceClientProxyImplementation.Patches.Implementations;

public partial class FormatHtmlCssPatch(ProxyConfiguration config) : IPatch {
    public int GetOrder() => -100;

    public string GetName() => "Format HTML/CSS file";
    public bool Applies(string relativeName, byte[] content) => relativeName.EndsWith(".css") || relativeName.EndsWith(".html");

    public async Task<byte[]> Execute(string relativeName, byte[] content) {
        var cachePath = Path.Combine(config.TestClient.RevisionPath, "formatted", relativeName);
        if (File.Exists(cachePath)) {
            Console.WriteLine($"Using cached formatted file for {relativeName}");
            return await File.ReadAllBytesAsync(cachePath);
        }

        Directory.CreateDirectory(Path.GetDirectoryName(cachePath)!);
        var tmpPath = $"{Environment.GetEnvironmentVariable("TMPDIR") ?? "/tmp"}/{Random.Shared.NextInt64()}_{Path.GetFileName(relativeName)}";
        await File.WriteAllBytesAsync(tmpPath, content);
        var sw = Stopwatch.StartNew();
        ProcessStartInfo psi = new ProcessStartInfo(config.AssetCache.PrettierPath, $"-w --print-width 240 {tmpPath}") {
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };
        
        using var process = Process.Start(psi);
        if (process == null) {
            throw new InvalidOperationException("Failed to start the formatting process.");
        }

        // var stdout = await process.StandardOutput.ReadToEndAsync();
        // var stderr = await process.StandardError.ReadToEndAsync();
        // await process.WaitForExitAsync();

        Dictionary<ulong, string> stdoutLines = new();
        Dictionary<ulong, string> stderrLines = new();
        
        while (!process.HasExited) {
            while (!process.StandardOutput.EndOfStream) {
                var line = await process.StandardOutput.ReadLineAsync();
                if (line == null) continue;
                stdoutLines[(ulong)sw.ElapsedMilliseconds] = line;
                Console.Write("O");
            }
            
            while (!process.StandardError.EndOfStream) {
                var line = await process.StandardError.ReadLineAsync();
                if (line == null) continue;
                stderrLines[(ulong)sw.ElapsedMilliseconds] = line;
                Console.Write("E");
            }
        }

        // Console.WriteLine($"Formatted {relativeName} in {sw.ElapsedMilliseconds}ms: {process.ExitCode}");

        if (process.ExitCode != 0) {
            Console.WriteLine($"Failed to format {relativeName} in {sw.ElapsedMilliseconds}ms: {process.ExitCode}");
            Console.WriteLine("Standard Output:\n" + string.Join("\n", stdoutLines.OrderBy(kv => kv.Key).Select(kv => $"[{kv.Key}ms] {kv.Value}")));
            Console.WriteLine("Standard Error:\n" + string.Join("\n", stderrLines.OrderBy(kv => kv.Key).Select(kv => $"[{kv.Key}ms] {kv.Value}")));
            throw new Exception($"Failed to format file {relativeName}: {string.Join("\n", stderrLines.OrderBy(kv => kv.Key).Select(kv => kv.Value))}");
        }

        var result = await File.ReadAllBytesAsync(tmpPath);
        File.Move(tmpPath, cachePath);
        return result;
    }
}