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/
|
.vscode/
|
||||||
builder/
|
builder/node/
|
||||||
build-scripts/
|
build-scripts/
|
||||||
.node_modules/
|
.node_modules/
|
||||||
package-lock.json
|
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