mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
Open source code of project builder
This commit is contained in:
parent
51789c3897
commit
5a748da280
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
.vscode/
|
||||
builder/
|
||||
builder/node/
|
||||
build-scripts/
|
||||
.node_modules/
|
||||
package-lock.json
|
||||
|
||||
8
builder/builder-config.json
Normal file
8
builder/builder-config.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"preview": "bilibili-evolved.preview.user.js",
|
||||
"offline": "bilibili-evolved.offline.user.js",
|
||||
"previewOffline": "bilibili-evolved.preview-offline.user.js",
|
||||
"master": "bilibili-evolved.user.js",
|
||||
"logoPath": "images/logo.png",
|
||||
"owner": "the1812"
|
||||
}
|
||||
91
builder/dotnet/BuildCache.cs
Normal file
91
builder/dotnet/BuildCache.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
namespace BilibiliEvolved.Build
|
||||
{
|
||||
class BuildCache : IDisposable
|
||||
{
|
||||
private readonly string fileName;
|
||||
private readonly ConcurrentDictionary<string, string> sha1Cache = new ConcurrentDictionary<string, string>();
|
||||
private readonly DirectoryInfo cacheDirectory;
|
||||
private readonly SHA1Managed sha1;
|
||||
|
||||
public BuildCache() : this(Environment.CurrentDirectory) {}
|
||||
public BuildCache(string path) : this(new DirectoryInfo(path)) {}
|
||||
public BuildCache(DirectoryInfo path)
|
||||
{
|
||||
cacheDirectory = path;
|
||||
fileName = Path.Combine(cacheDirectory.FullName, "build.cache");
|
||||
sha1 = new SHA1Managed();
|
||||
LoadCache();
|
||||
}
|
||||
|
||||
private string hashToString(byte[] hash)
|
||||
{
|
||||
return string.Join("", hash.Select(b => b.ToString("X2")).ToArray());
|
||||
}
|
||||
private string getFileHashString(string path)
|
||||
{
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
return hashToString(sha1.ComputeHash(bytes));
|
||||
}
|
||||
|
||||
public void LoadCache()
|
||||
{
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
var lines = File.ReadAllText(fileName)
|
||||
.Split(Environment.NewLine)
|
||||
.Where(l => !string.IsNullOrWhiteSpace(l));
|
||||
lines.ForEach(line =>
|
||||
{
|
||||
var data = line.Split("|");
|
||||
var fileName = data[0];
|
||||
var sha1Text = data[1];
|
||||
sha1Cache.TryAdd(fileName, sha1Text);
|
||||
});
|
||||
}
|
||||
}
|
||||
public void SaveCache()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
sha1Cache.ForEach(pair =>
|
||||
{
|
||||
builder
|
||||
.Append(pair.Key)
|
||||
.Append("|")
|
||||
.Append(pair.Value)
|
||||
.Append(Environment.NewLine);
|
||||
});
|
||||
File.WriteAllText(fileName, builder.ToString().Trim());
|
||||
}
|
||||
|
||||
public void AddCache(string file)
|
||||
{
|
||||
var hashText = getFileHashString(file);
|
||||
sha1Cache.AddOrUpdate(file, hashText, (k, v) => hashText);
|
||||
}
|
||||
public bool Contains(string file)
|
||||
{
|
||||
var hashText = getFileHashString(file);
|
||||
if (sha1Cache.ContainsKey(file))
|
||||
{
|
||||
return sha1Cache[file] == hashText;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
sha1?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
16
builder/dotnet/BuilderConfig.cs
Normal file
16
builder/dotnet/BuilderConfig.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BilibiliEvolved.Build
|
||||
{
|
||||
class BuilderConfig
|
||||
{
|
||||
public string Master { get; set; }
|
||||
public string Preview { get; set; }
|
||||
public string Offline { get; set; }
|
||||
public string PreviewOffline { get; set; }
|
||||
public string LogoPath { get; set; }
|
||||
public string Owner { get; set; }
|
||||
}
|
||||
}
|
||||
21
builder/dotnet/DarkStyleBuilder.cs
Normal file
21
builder/dotnet/DarkStyleBuilder.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
namespace BilibiliEvolved.Build
|
||||
{
|
||||
partial class ProjectBuilder
|
||||
{
|
||||
public ProjectBuilder BuildDarkStyles()
|
||||
{
|
||||
var files = new DirectoryInfo("min").EnumerateFiles().Where(f => f.FullName.Contains("dark-slice"));
|
||||
var fullStyle = files.Select(f => File.ReadAllText(f.FullName))
|
||||
.Aggregate((acc, s) => acc + s);
|
||||
File.WriteAllText("min/dark.min.scss", fullStyle);
|
||||
WriteSuccess("Dark style build complete.");
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
builder/dotnet/Extensions.cs
Normal file
18
builder/dotnet/Extensions.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BilibiliEvolved.Build
|
||||
{
|
||||
static class Extensions
|
||||
{
|
||||
public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action)
|
||||
{
|
||||
foreach (var item in collection)
|
||||
{
|
||||
action(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
32
builder/dotnet/MasterBuilder.cs
Normal file
32
builder/dotnet/MasterBuilder.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BilibiliEvolved.Build
|
||||
{
|
||||
partial class ProjectBuilder
|
||||
{
|
||||
private readonly Regex ownerRegex = new Regex(@"(github\.com/)(.+)(/Bilibili-Evolved)");
|
||||
public ProjectBuilder BuildMaster()
|
||||
{
|
||||
var master = Source;
|
||||
master = ownerRegex.Replace(master, "${1}" + config.Owner + "${3}");
|
||||
var replaceMap = new Dictionary<string, string>
|
||||
{
|
||||
{ @"Bilibili-Evolved/preview", @"Bilibili-Evolved/master" },
|
||||
{ @"Bilibili-Evolved/raw/preview", @"Bilibili-Evolved/raw/master" },
|
||||
{ SourcePath, OutputPath },
|
||||
{ @"// settings.guiSettings = true;", @"settings.guiSettings = true;" },
|
||||
{ @"Bilibili Evolved (Preview)", @"Bilibili Evolved" },
|
||||
{ @"增强哔哩哔哩Web端体验(预览版分支):", @"增强哔哩哔哩Web端体验:" },
|
||||
{ @"settings.debug = true;", @"settings.debug = false;" },
|
||||
};
|
||||
replaceMap.ForEach(item => master = master.Replace(item.Key, item.Value));
|
||||
Output = master;
|
||||
WriteSuccess("Master build complete.");
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
114
builder/dotnet/OfflineBuilder.cs
Normal file
114
builder/dotnet/OfflineBuilder.cs
Normal file
@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BilibiliEvolved.Build
|
||||
{
|
||||
partial class ProjectBuilder
|
||||
{
|
||||
private string offlineText;
|
||||
private string offlineVersion;
|
||||
|
||||
private void replaceInfo(Dictionary<string, string> map)
|
||||
{
|
||||
var logoBytes = File.ReadAllBytes(config.LogoPath);
|
||||
var logoBase64 = $"data:image/png;base64,{Convert.ToBase64String(logoBytes)}";
|
||||
map.ForEach(item => offlineText = offlineText.Replace(item.Key, item.Value));
|
||||
offlineText = ownerRegex.Replace(offlineText, "${1}" + config.Owner + "${3}");
|
||||
offlineText = offlineText.Replace(
|
||||
$"@icon https://raw.githubusercontent.com/{config.Owner}/Bilibili-Evolved/master/images/logo.png",
|
||||
$"@icon {logoBase64}");
|
||||
}
|
||||
private void generateVersion()
|
||||
{
|
||||
var startDate = new DateTime(2018, 7, 16, 15, 46, 53);
|
||||
var versionRegex = new Regex(@"(@version[ ]*)[\d\.]*");
|
||||
var version = DateTime.Now.ToOADate() - startDate.ToOADate();
|
||||
offlineVersion = version.ToString("0.00");
|
||||
offlineText = versionRegex.Replace(offlineText, "${1}" + offlineVersion);
|
||||
}
|
||||
private void compileOfflineData()
|
||||
{
|
||||
var onlineRoot = new Regex(@"Resource.root = ""(.*)"";").Match(offlineText).Groups[1].Value;
|
||||
var urlList = (from match in new Regex(@"path:\s*""(.*)""").Matches(offlineText)
|
||||
as IEnumerable<Match>
|
||||
select match.Groups[1].Value.Trim()
|
||||
).ToList();
|
||||
|
||||
var downloadCodeStart = @"// \+#Offline build placeholder";
|
||||
var downloadCodeEnd = @"// \-#Offline build placeholder";
|
||||
var downloadCodes = new Regex($"({downloadCodeStart}([^\0]*){downloadCodeEnd})").Match(offlineText).Groups[0].Value;
|
||||
|
||||
var offlineData = "const offlineData = {};" + Environment.NewLine;
|
||||
foreach (var url in urlList)
|
||||
{
|
||||
var text = File.ReadAllText(url);
|
||||
if (url.EndsWith(".js"))
|
||||
{
|
||||
offlineData = offlineData + $"offlineData[\"{onlineRoot + url}\"] = {text}" + Environment.NewLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
offlineData = offlineData + $"offlineData[\"{onlineRoot + url}\"] = `{text}`;" + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
offlineText = offlineText
|
||||
.Replace(@"// [Offline build placeholder]", offlineData)
|
||||
.Replace(downloadCodes, "this.text=this.type.preprocessor(offlineData[this.url]);resolve(this.text);");
|
||||
}
|
||||
private void buildFile(string path)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var offlineFileText = File.ReadAllText(path);
|
||||
|
||||
var noVersion = new Regex(@"// @version[ ]*(.*)" + Environment.NewLine);
|
||||
var originalOffline = noVersion.Replace(offlineFileText, "");
|
||||
var currentOffline = noVersion.Replace(offlineText, "");
|
||||
|
||||
if (currentOffline == originalOffline)
|
||||
{
|
||||
offlineVersion = noVersion.Match(offlineFileText).Groups[1].Value.Trim();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllText(path, offlineText);
|
||||
}
|
||||
private ProjectBuilder build(Dictionary<string, string> replaceMap, string outputPath, string successMessage)
|
||||
{
|
||||
offlineText = Output;
|
||||
replaceInfo(replaceMap);
|
||||
generateVersion();
|
||||
compileOfflineData();
|
||||
buildFile(outputPath);
|
||||
|
||||
WriteSuccess(successMessage);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ProjectBuilder BuildOffline()
|
||||
{
|
||||
var replaceMap = new Dictionary<string, string>
|
||||
{
|
||||
{ "Bilibili Evolved", "Bilibili Evolved (Offline)" },
|
||||
{ "增强哔哩哔哩Web端体验:", "增强哔哩哔哩Web端体验(离线版):" },
|
||||
{ $"master/{config.Master}", $"master/{config.Offline}" },
|
||||
};
|
||||
return build(replaceMap, config.Offline, "Offline build complete.");
|
||||
}
|
||||
public ProjectBuilder BuildPreviewOffline()
|
||||
{
|
||||
var replaceMap = new Dictionary<string, string>
|
||||
{
|
||||
{ "Bilibili Evolved", "Bilibili Evolved (Preview Offline)" },
|
||||
{ "增强哔哩哔哩Web端体验:", "增强哔哩哔哩Web端体验(预览离线版):" },
|
||||
{ $"master/{config.Master}", $"preview/{config.PreviewOffline}" },
|
||||
};
|
||||
return build(replaceMap, config.PreviewOffline, "Preview Offline build complete.");
|
||||
}
|
||||
}
|
||||
}
|
||||
21
builder/dotnet/PreviewBuilder.cs
Normal file
21
builder/dotnet/PreviewBuilder.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BilibiliEvolved.Build
|
||||
{
|
||||
partial class ProjectBuilder
|
||||
{
|
||||
public ProjectBuilder BuildPreview()
|
||||
{
|
||||
var version = new Regex(@"//[ ]*@version[ ]*(.+)")
|
||||
.Match(Source).Groups[1].Value.Trim();
|
||||
Source = ownerRegex.Replace(Source, "${1}" + config.Owner + "${3}");
|
||||
File.WriteAllText(SourcePath, Source);
|
||||
File.WriteAllText("version.txt", version);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
builder/dotnet/Program.cs
Normal file
43
builder/dotnet/Program.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace BilibiliEvolved.Build
|
||||
{
|
||||
class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var configFile = new ConfigurationBuilder()
|
||||
.AddJsonFile(
|
||||
Path.Combine(Environment.CurrentDirectory, "builder/builder-config.json"),
|
||||
optional: false,
|
||||
reloadOnChange: false)
|
||||
.Build();
|
||||
var config = new BuilderConfig();
|
||||
configFile.Bind(config);
|
||||
|
||||
var builder = new ProjectBuilder(config);
|
||||
builder
|
||||
.BuildPreview()
|
||||
.BuildMaster()
|
||||
.BuildResources()
|
||||
.BuildPreviewOffline()
|
||||
.BuildOffline()
|
||||
.BuildFinalOutput();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.Error.WriteLine($"Unexcepted Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
builder/dotnet/ProjectBuilder.cs
Normal file
47
builder/dotnet/ProjectBuilder.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BilibiliEvolved.Build
|
||||
{
|
||||
partial class ProjectBuilder
|
||||
{
|
||||
public ProjectBuilder(BuilderConfig config)
|
||||
{
|
||||
this.config = config;
|
||||
SourcePath = config.Preview;
|
||||
Source = File.ReadAllText(SourcePath);
|
||||
WriteInfo("[Bilibili Evolved] Project builder started.");
|
||||
WriteInfo($"Working directory: {Environment.CurrentDirectory}");
|
||||
WriteInfo();
|
||||
}
|
||||
private BuilderConfig config;
|
||||
public double MinifiedResourceLength { get; set; }
|
||||
public double OriginalResourceLength { get; set; }
|
||||
public string Source { get; private set; }
|
||||
public string Output { get; private set; }
|
||||
public string SourcePath { get; private set; }
|
||||
public string OutputPath { get; set; } = "bilibili-evolved.user.js";
|
||||
public void BuildFinalOutput()
|
||||
{
|
||||
var ratio = 100.0 * MinifiedResourceLength / OriginalResourceLength;
|
||||
File.WriteAllText(OutputPath, Output);
|
||||
WriteInfo();
|
||||
WriteHint($"External resource size -{(100.0 - ratio):0.##}%");
|
||||
WriteInfo("Build complete.", ConsoleColor.Green);
|
||||
}
|
||||
public void WriteInfo(string message = "", ConsoleColor color = ConsoleColor.Gray)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
Console.ForegroundColor = color;
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
}
|
||||
public void WriteSuccess(string message) => WriteInfo(message, ConsoleColor.Blue);
|
||||
public void WriteError(string message) => WriteInfo(message, ConsoleColor.Red);
|
||||
public void WriteHint(string message) => WriteInfo(message, ConsoleColor.DarkGray);
|
||||
}
|
||||
}
|
||||
241
builder/dotnet/ResourceBuilder.cs
Normal file
241
builder/dotnet/ResourceBuilder.cs
Normal file
@ -0,0 +1,241 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BilibiliEvolved.Build
|
||||
{
|
||||
partial class ProjectBuilder
|
||||
{
|
||||
public ProjectBuilder BuildResources()
|
||||
{
|
||||
new CssMinifier().Build(this);
|
||||
new HtmlMinifier().Build(this);
|
||||
new JavascriptMinifier().Build(this);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
abstract class ResourceMinifier
|
||||
{
|
||||
public abstract Predicate<FileInfo> FileFilter { get; }
|
||||
public abstract string ResouceType { get; }
|
||||
public abstract string Minify(string input);
|
||||
protected string GetMinifiedFileName(string path)
|
||||
{
|
||||
var fileInfo = new FileInfo(path);
|
||||
return "min/" + fileInfo.Name.Insert(fileInfo.Name.LastIndexOf("."), ".min");
|
||||
}
|
||||
protected IEnumerable<string> GetFiles(Predicate<FileInfo> filter)
|
||||
{
|
||||
string getRelativePath(string fullPath)
|
||||
{
|
||||
var currentFolder = Environment.CurrentDirectory;
|
||||
var path = fullPath.Replace(currentFolder, "");
|
||||
if (path.StartsWith(Path.DirectorySeparatorChar))
|
||||
{
|
||||
return path.Remove(path.IndexOf(Path.DirectorySeparatorChar), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
IEnumerable<string> getFiles(Predicate<FileInfo> predicate, string path)
|
||||
{
|
||||
var list = new List<string>();
|
||||
var currentDirectory = new DirectoryInfo(path);
|
||||
list.AddRange(currentDirectory.EnumerateFiles()
|
||||
.Where(file => predicate(file))
|
||||
.Select(file => getRelativePath(file.FullName)));
|
||||
foreach (var subDir in currentDirectory.EnumerateDirectories())
|
||||
{
|
||||
list.AddRange(getFiles(filter, subDir.FullName));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
var directory = Environment.CurrentDirectory;
|
||||
if (!directory.EndsWith(Path.DirectorySeparatorChar))
|
||||
{
|
||||
directory = directory + Path.DirectorySeparatorChar;
|
||||
}
|
||||
return getFiles(filter, directory);
|
||||
}
|
||||
public virtual ProjectBuilder Build(ProjectBuilder builder)
|
||||
{
|
||||
var files = GetFiles(file =>
|
||||
FileFilter(file)
|
||||
&& !file.FullName.Contains(@".vs\")
|
||||
&& !file.FullName.Contains(@".vscode\")
|
||||
&& !file.FullName.Contains(@"build-scripts\")
|
||||
&& !file.FullName.Contains(@"node_modules\")
|
||||
);
|
||||
using (var cache = new BuildCache())
|
||||
{
|
||||
var changedFiles = files.Where(file => !cache.Contains(file));
|
||||
Parallel.ForEach(changedFiles, path =>
|
||||
{
|
||||
builder.WriteInfo($"{ResouceType} minify: {path}");
|
||||
|
||||
var text = File.ReadAllText(path);
|
||||
var result = Minify(text);
|
||||
|
||||
var outputPath = GetMinifiedFileName(path);
|
||||
File.WriteAllText(outputPath, result);
|
||||
cache.AddCache(path);
|
||||
|
||||
builder.WriteHint($"\t=> {outputPath.PadRight(48)}{(100.0 * result.Length / text.Length):0.##}%");
|
||||
});
|
||||
cache.SaveCache();
|
||||
}
|
||||
files.ForEach(file =>
|
||||
{
|
||||
builder.OriginalResourceLength += new FileInfo(file).Length;
|
||||
builder.MinifiedResourceLength += new FileInfo(GetMinifiedFileName(file)).Length;
|
||||
});
|
||||
builder.WriteSuccess($"{ResouceType} minify complete.");
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
sealed class CssMinifier : ResourceMinifier
|
||||
{
|
||||
public override Predicate<FileInfo> FileFilter { get; } = file =>
|
||||
{
|
||||
return !file.FullName.Contains(".min")
|
||||
&& !file.FullName.Contains("dark.scss")
|
||||
&& !file.FullName.Contains("dark-template")
|
||||
&& (file.Extension == ".css" || file.Extension == ".scss");
|
||||
};
|
||||
|
||||
public override string ResouceType { get; } = "CSS";
|
||||
|
||||
public override string Minify(string input)
|
||||
{
|
||||
var commentRegex = new Regex(@"/\*[^\0]*?\*/|^\s*//.*$", RegexOptions.Multiline);
|
||||
input = commentRegex.Replace(input, "");
|
||||
|
||||
var selectorRegex = new Regex(@"[\s]*([^\0,}]+?)[\s]*({)|[\s]*([^\0}]+?)(,)" + Environment.NewLine);
|
||||
input = selectorRegex.Replace(input, "$1$2$3$4");
|
||||
|
||||
var ruleRegex = new Regex(@"[\s]*([a-z\-]+:)[ ]*(.*?)[ ]*(!important)?;[\s]*");
|
||||
input = ruleRegex.Replace(input, "$1$2$3;");
|
||||
|
||||
return input
|
||||
.Replace(Environment.NewLine, "")
|
||||
.Replace("\n", "")
|
||||
.Replace("\r", "")
|
||||
.Replace(", ", ",");
|
||||
}
|
||||
|
||||
public override ProjectBuilder Build(ProjectBuilder builder)
|
||||
{
|
||||
base.Build(builder);
|
||||
return builder.BuildDarkStyles();
|
||||
}
|
||||
}
|
||||
sealed class JavascriptMinifier : ResourceMinifier
|
||||
{
|
||||
private static readonly string UglifyEsArguments = @"-m";
|
||||
private static readonly string NodePath = "node";
|
||||
|
||||
private static string uglifyEsAbsolutePath = null;
|
||||
private static string UglifyEsAbsolutePath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (uglifyEsAbsolutePath is null)
|
||||
{
|
||||
uglifyEsAbsolutePath = getUglifyEsPath();
|
||||
}
|
||||
return uglifyEsAbsolutePath;
|
||||
}
|
||||
set => uglifyEsAbsolutePath = value;
|
||||
}
|
||||
private static string getUglifyEsPath()
|
||||
{
|
||||
var uglifyEsPath = @"\uglify-es\bin\uglifyjs";
|
||||
var localPath = @"\node_modules" + uglifyEsPath;
|
||||
var globalPath = Environment.GetEnvironmentVariable("AppData") + @"\npm\node_modules" + uglifyEsPath;
|
||||
|
||||
if (File.Exists(localPath))
|
||||
{
|
||||
return localPath;
|
||||
}
|
||||
else if (File.Exists(globalPath))
|
||||
{
|
||||
return globalPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public JavascriptMinifier()
|
||||
{
|
||||
if (!File.Exists(UglifyEsAbsolutePath))
|
||||
{
|
||||
throw new FileNotFoundException("Node.js module \"uglify-es\" not found.");
|
||||
}
|
||||
}
|
||||
|
||||
public override Predicate<FileInfo> FileFilter { get; } = file =>
|
||||
{
|
||||
return !file.FullName.Contains(".min")
|
||||
&& !file.FullName.Contains(@"builder\")
|
||||
&& !file.FullName.Contains("bilibili-evolved.")
|
||||
&& file.Extension == ".js";
|
||||
};
|
||||
|
||||
public override string ResouceType { get; } = "JavaScript";
|
||||
|
||||
public override string Minify(string input)
|
||||
{
|
||||
var processInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = NodePath,
|
||||
Arguments = $"{UglifyEsAbsolutePath} {UglifyEsArguments}",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
};
|
||||
var process = Process.Start(processInfo);
|
||||
using (var writer = new StreamWriter(process.StandardInput.BaseStream, Encoding.UTF8))
|
||||
{
|
||||
writer.Write(input);
|
||||
writer.Flush();
|
||||
writer.Close();
|
||||
using (var reader = new StreamReader(process.StandardOutput.BaseStream, Encoding.UTF8))
|
||||
{
|
||||
return reader.ReadToEnd().Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sealed class HtmlMinifier : ResourceMinifier
|
||||
{
|
||||
public override Predicate<FileInfo> FileFilter { get; } = file =>
|
||||
{
|
||||
return !file.FullName.Contains(".min") && file.Extension == ".html";
|
||||
};
|
||||
|
||||
public override string ResouceType { get; } = "HTML";
|
||||
|
||||
public override string Minify(string input)
|
||||
{
|
||||
var commentRegex = new Regex(@"<!--[^\0]*?-->", RegexOptions.Multiline);
|
||||
input = commentRegex.Replace(input, "");
|
||||
|
||||
var blankRegex = new Regex(@"(?<=>)[\s]*([^\s]*)[\s]*(?=<)|[\s]*(?=/>)", RegexOptions.Multiline);
|
||||
input = blankRegex.Replace(input, "$1");
|
||||
|
||||
return input
|
||||
.Replace(Environment.NewLine, "")
|
||||
.Replace("\n", "")
|
||||
.Replace("\r", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
18
builder/dotnet/dotnet.csproj
Normal file
18
builder/dotnet/dotnet.csproj
Normal file
@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Authors>Grant Howard</Authors>
|
||||
<Product>Bilibili-Evolved Project Builder</Product>
|
||||
<StartupObject>BilibiliEvolved.Build.Program</StartupObject>
|
||||
<AssemblyName>build</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Loading…
Reference in New Issue
Block a user