Files
ds5678 47bc440c76 Update Bootstrap to 5.3.8 and Vue.js to 3.5.27
Updated Bootstrap CDN links and integrity hashes to version 5.3.8. Also updated Vue.js CDN links to version 3.5.27 for both production and development builds.
2026-01-26 20:06:38 -08:00

188 lines
6.3 KiB
C#

using AssetRipper.Text.SourceGeneration;
using Microsoft.CodeAnalysis.CSharp;
using System.CodeDom.Compiler;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
namespace AssetRipper.GUI.Web.Dependencies.SourceGenerator;
public static class Program
{
private readonly record struct Dependency(string Name, string Website, string ProductionDownloadUrl, string? ProductionIntegrity, string? DevelopmentDownloadUrl, string? DevelopmentIntegrity, string Path, string MimeType);
private static readonly Dependency[] dependencies =
[
new(
"BabylonMain",
"https://www.babylonjs.com/",
"https://cdn.babylonjs.com/babylon.js",
null,
null,
null,
"/js/babylon.js",
"application/javascript"),
new(
"BabylonLoader",
"https://www.babylonjs.com/",
"https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js",
null,
null,
null,
"/js/babylonjs.loaders.min.js",
"application/javascript"),
new(
"BootstrapStyleSheet",
"https://getbootstrap.com/",
"https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css",
"sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB",
null,
null,
"/css/bootstrap.min.css",
"text/css"),
new(
"BootstrapScript",
"https://getbootstrap.com/",
"https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js",
"sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI",
null,
null,
"/js/bootstrap.bundle.min.js",
"application/javascript"),
new(
"Popper",
"https://jquery.com/",
"https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js",
"sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r",
null,
null,
"/js/popper.min.js",
"application/javascript"),
new(
"Vue",
"https://vuejs.org/",
"https://unpkg.com/[email protected]/dist/vue.global.prod.js",
null,
"https://unpkg.com/[email protected]/dist/vue.global.js",
null,
"/js/vue.js",
"application/javascript"),
];
public static void Main(string[] args)
{
string outputDirectory = args[0];
using HttpClient client = CreateClient();
string outputPath = Path.Combine(outputDirectory, "Dependencies");
IndentedTextWriter writer = IndentedTextWriterFactory.Create(outputDirectory, "EmbeddedDependency");
writer.WriteGeneratedCodeWarning();
writer.WriteLineNoTabs();
writer.WriteUsing("System.Text");
writer.WriteLineNoTabs();
writer.WriteFileScopedNamespace("AssetRipper.GUI.Web.Dependencies");
writer.WriteLineNoTabs();
writer.WriteLine("public readonly record struct EmbeddedDependency(string Content, string Path, string MimeType, bool IsDevelopment)");
using (new CurlyBrackets(writer))
{
writer.WriteLine("public bool IsScript => MimeType == \"application/javascript\";");
writer.WriteLine("public bool IsStyleSheet => MimeType == \"text/css\";");
writer.WriteLine("public bool IsProduction => !IsDevelopment;");
foreach (Dependency dependency in dependencies)
{
writer.WriteSummaryDocumentation($"<see href={GetLiteral(dependency.Website)} />");
WriteProperty(client, writer, dependency.Name, dependency.ProductionDownloadUrl, dependency.ProductionIntegrity, dependency.Path, dependency.MimeType, false);
if (dependency.DevelopmentDownloadUrl is null)
{
writer.WriteLine($"internal static EmbeddedDependency {dependency.Name}Development => {dependency.Name};");
}
else
{
writer.WriteSummaryDocumentation($"<see href={GetLiteral(dependency.Website)} />");
WriteProperty(client, writer, dependency.Name + "Development", dependency.DevelopmentDownloadUrl, dependency.DevelopmentIntegrity, dependency.Path, dependency.MimeType, true);
}
}
writer.WriteLine("public static IReadOnlyList<EmbeddedDependency> GetAllProduction() =>");
writer.WriteLine('[');
using (new Indented(writer))
{
foreach (Dependency dependency in dependencies)
{
writer.WriteLine($"{dependency.Name},");
}
}
writer.WriteLine("];");
writer.WriteLine("public static IReadOnlyList<EmbeddedDependency> GetAllDevelopment() =>");
writer.WriteLine('[');
using (new Indented(writer))
{
foreach (Dependency dependency in dependencies)
{
writer.WriteLine($"{dependency.Name}Development,");
}
}
writer.WriteLine("];");
}
}
private static void WriteProperty(HttpClient client, IndentedTextWriter writer, string name, string url, string? integrity, string path, string mimeType, bool isDevelopment)
{
writer.WriteLine($"public static EmbeddedDependency {name}");
using (new CurlyBrackets(writer))
{
writer.WriteLine("get");
using (new CurlyBrackets(writer))
{
using (new If(writer, "field == default"))
{
byte[] data = DownloadAsync(client, url, integrity).GetAwaiter().GetResult();
string content = GetLiteral(Encoding.UTF8.GetString(data));
string isDevelopmentString = isDevelopment ? "true" : "false";
writer.WriteLine($"ReadOnlySpan<byte> data = {content}u8;");
writer.WriteLine($"field = new(Encoding.UTF8.GetString(data), {GetLiteral(path)}, {GetLiteral(mimeType)}, {isDevelopmentString});");
}
writer.WriteLine("return field;");
}
}
}
private static HttpClient CreateClient()
{
HttpClient client = new();
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("AssetRipper.GUI.Web.Dependencies.SourceGenerator", "1.0"));
return client;
}
private static async Task<byte[]> DownloadAsync(HttpClient client, string url, string? integrity)
{
using HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
byte[] result = await response.Content.ReadAsByteArrayAsync();
if (!ValidateIntegrity(result, integrity))
{
throw new InvalidOperationException($"Integrity check failed for {url}");
}
return result;
}
private static bool ValidateIntegrity(byte[] data, string? integrity)
{
if (string.IsNullOrEmpty(integrity))
{
return true;
}
const string Sha384Prefix = "sha384-";
if (integrity.StartsWith(Sha384Prefix, StringComparison.Ordinal))
{
byte[] hash = SHA384.HashData(data);
byte[] integrityHash = Convert.FromBase64String(integrity[Sha384Prefix.Length..]);
return hash.SequenceEqual(integrityHash);
}
else
{
return false;
}
}
private static string GetLiteral(string content) => SymbolDisplay.FormatLiteral(content, true);
}