Prepared tailwind system for plugin builds and exports via nuget. Removed obsolete old css bundling. Added helper scripts for building. Rewritten build scripts
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -428,4 +428,8 @@ core.min.css
|
||||
|
||||
# Build script for nuget packages
|
||||
finalPackages/
|
||||
nupkgs/
|
||||
nupkgs/
|
||||
|
||||
# Scripts
|
||||
**/bin/**
|
||||
**/obj/**
|
||||
@@ -10,6 +10,5 @@ public class PluginManifest
|
||||
public string[] Scripts { get; set; } = [];
|
||||
public string[] Styles { get; set; } = [];
|
||||
|
||||
public string[] BundledStyles { get; set; } = [];
|
||||
public Dictionary<string, string[]> Assemblies { get; set; } = new();
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
<ItemGroup>
|
||||
<Content Include="..\.dockerignore">
|
||||
<Link>.dockerignore</Link>
|
||||
<Pack>false</Pack>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
using ExCSS;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using MoonCore.Helpers;
|
||||
|
||||
namespace Moonlight.ApiServer.Services;
|
||||
|
||||
public class BundleGenerationService : IHostedService
|
||||
{
|
||||
private readonly ILogger<BundleGenerationService> Logger;
|
||||
private readonly IWebHostEnvironment HostEnvironment;
|
||||
private readonly PluginService PluginService;
|
||||
private readonly BundleService BundleService;
|
||||
|
||||
public BundleGenerationService(
|
||||
ILogger<BundleGenerationService> logger,
|
||||
IWebHostEnvironment hostEnvironment,
|
||||
BundleService bundleService,
|
||||
PluginService pluginService
|
||||
)
|
||||
{
|
||||
Logger = logger;
|
||||
HostEnvironment = hostEnvironment;
|
||||
BundleService = bundleService;
|
||||
PluginService = pluginService;
|
||||
}
|
||||
|
||||
private async Task Bundle(CancellationToken cancellationToken)
|
||||
{
|
||||
Logger.LogInformation("Bundling css files...");
|
||||
|
||||
// Search the physical path for the defined files
|
||||
var physicalCssFiles = new List<string>();
|
||||
|
||||
foreach (var cssFile in BundleService.GetCssFiles())
|
||||
{
|
||||
var fileInfo = HostEnvironment.WebRootFileProvider.GetFileInfo(cssFile);
|
||||
|
||||
if (fileInfo is NotFoundFileInfo || fileInfo.PhysicalPath == null)
|
||||
{
|
||||
fileInfo = PluginService.WwwRootFileProvider.GetFileInfo(cssFile);
|
||||
|
||||
if (fileInfo is NotFoundFileInfo || fileInfo.PhysicalPath == null)
|
||||
{
|
||||
Logger.LogWarning(
|
||||
"Unable to find physical path for the requested css file '{file}'. Make sure its inside a wwwroot folder",
|
||||
cssFile
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogTrace("Discovered css file '{path}' at '{physicalPath}'", cssFile, fileInfo.PhysicalPath);
|
||||
|
||||
physicalCssFiles.Add(fileInfo.PhysicalPath);
|
||||
}
|
||||
|
||||
if (physicalCssFiles.Count == 0)
|
||||
Logger.LogWarning(
|
||||
"No physical paths to css files loaded. The generated bundle will be empty. Unless this is intended by you this is a bug");
|
||||
|
||||
// TODO: Implement cache
|
||||
|
||||
// TODO: File system watcher for development
|
||||
|
||||
var bundleContent = await CreateCssBundle(physicalCssFiles);
|
||||
|
||||
Directory.CreateDirectory(PathBuilder.Dir("storage", "tmp"));
|
||||
|
||||
await File.WriteAllTextAsync(PathBuilder.File("storage", "tmp", "bundle.css"), bundleContent,
|
||||
cancellationToken);
|
||||
|
||||
Logger.LogInformation("Successfully built css bundle");
|
||||
}
|
||||
|
||||
private async Task<string> CreateCssBundle(List<string> physicalPaths)
|
||||
{
|
||||
if (physicalPaths.Count == 0) // No stylesheets defined => nothing to process
|
||||
return "";
|
||||
|
||||
if (physicalPaths.Count == 1) // Only one stylesheet => nothing to process
|
||||
return await File.ReadAllTextAsync(physicalPaths[0]);
|
||||
|
||||
// Simple bundler just to test
|
||||
var result = "";
|
||||
|
||||
foreach (var path in physicalPaths)
|
||||
{
|
||||
result += await File.ReadAllTextAsync(path);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
// Create bundle by stripping out double declared classes and combining all css files into one bundle
|
||||
var parser = new StylesheetParser();
|
||||
string? content = null;
|
||||
Stylesheet? mainStylesheet = null;
|
||||
var additionalStyleSheets = new List<Stylesheet>();
|
||||
|
||||
foreach (var physicalPath in physicalPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileContent = await File.ReadAllTextAsync(physicalPath);
|
||||
var stylesheet = await parser.ParseAsync(fileContent);
|
||||
|
||||
// Check if it's the first stylesheet we are loading
|
||||
if (mainStylesheet == null || content == null)
|
||||
{
|
||||
// Delegate the first stylesheet to be the main one
|
||||
content = fileContent + "\n";
|
||||
mainStylesheet = stylesheet;
|
||||
}
|
||||
else
|
||||
additionalStyleSheets.Add(stylesheet); // All other stylesheets are to be processed
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError("An error occured while parsing css file: {e}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle an empty main stylesheet delegation
|
||||
if (mainStylesheet == null || content == null)
|
||||
{
|
||||
Logger.LogError("An unable to delegate main stylesheet. Did every load attempt of an stylesheet fail?");
|
||||
return "";
|
||||
}
|
||||
|
||||
// Process stylesheets against the main one
|
||||
foreach (var stylesheet in additionalStyleSheets)
|
||||
{
|
||||
// Style
|
||||
foreach (var styleRule in stylesheet.StyleRules)
|
||||
{
|
||||
if (mainStylesheet.StyleRules.Any(x => x.Selector.Text == styleRule.Selector.Text))
|
||||
continue;
|
||||
|
||||
content += styleRule.StylesheetText.Text + "\n";
|
||||
}
|
||||
|
||||
// Container
|
||||
foreach (var containerRule in stylesheet.ContainerRules)
|
||||
{
|
||||
if (mainStylesheet.ContainerRules.Any(x => x.ConditionText == containerRule.ConditionText))
|
||||
continue;
|
||||
|
||||
content += containerRule.StylesheetText.Text + "\n";
|
||||
}
|
||||
|
||||
// Import Rule
|
||||
foreach (var importRule in stylesheet.ImportRules)
|
||||
{
|
||||
if (mainStylesheet.ImportRules.Any(x => x.Text == importRule.Text))
|
||||
continue;
|
||||
|
||||
content = importRule.StylesheetText.Text + "\n" + content;
|
||||
}
|
||||
|
||||
// Media Rules
|
||||
foreach (var mediaRule in stylesheet.MediaRules)
|
||||
content += mediaRule.StylesheetText.Text + "\n";
|
||||
|
||||
// Page Rules
|
||||
foreach (var pageRule in stylesheet.PageRules)
|
||||
{
|
||||
if (mainStylesheet.PageRules.Any(x => x.SelectorText == pageRule.SelectorText))
|
||||
continue;
|
||||
|
||||
content += pageRule.StylesheetText.Text + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
//
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
=> Bundle(cancellationToken);
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Moonlight.ApiServer.Services;
|
||||
|
||||
public class BundleService
|
||||
{
|
||||
private readonly List<string> CssFiles = new();
|
||||
|
||||
public void BundleCss(string path)
|
||||
=> CssFiles.Add(path);
|
||||
|
||||
public void BundleCssRange(string[] paths)
|
||||
=> CssFiles.AddRange(paths);
|
||||
|
||||
public IEnumerable<string> GetCssFiles() => CssFiles;
|
||||
}
|
||||
@@ -54,9 +54,6 @@ public class Startup
|
||||
private PluginService PluginService;
|
||||
private AssemblyLoadContext PluginLoadContext;
|
||||
|
||||
// Asset bundling
|
||||
private BundleService BundleService = new();
|
||||
|
||||
private IPluginStartup[] PluginStartups;
|
||||
|
||||
public async Task Run(string[] args, Assembly[]? additionalAssemblies = null,
|
||||
@@ -71,7 +68,6 @@ public class Startup
|
||||
await CreateStorage();
|
||||
await SetupAppConfiguration();
|
||||
await SetupLogging();
|
||||
await SetupBundling();
|
||||
await LoadPlugins();
|
||||
await InitializePlugins();
|
||||
|
||||
@@ -132,15 +128,6 @@ public class Startup
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task SetupBundling()
|
||||
{
|
||||
BundleService = new();
|
||||
|
||||
BundleService.BundleCss("css/core.min.css");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
#region Base
|
||||
|
||||
private Task RegisterBase()
|
||||
@@ -257,13 +244,6 @@ public class Startup
|
||||
// Configure base services for initialisation
|
||||
startupSc.AddSingleton(Configuration);
|
||||
|
||||
// Add bundle service so plugins can do additional bundling if required
|
||||
startupSc.AddSingleton(BundleService);
|
||||
|
||||
// Auto add all files specified in the bundledStyles section to the bundle job
|
||||
foreach (var plugin in PluginService.LoadedPlugins.Keys)
|
||||
BundleService.BundleCssRange(plugin.BundledStyles);
|
||||
|
||||
startupSc.AddLogging(builder =>
|
||||
{
|
||||
builder.ClearProviders();
|
||||
@@ -308,10 +288,6 @@ public class Startup
|
||||
|
||||
private Task RegisterPluginAssets()
|
||||
{
|
||||
WebApplicationBuilder.Services.AddHostedService(sp => sp.GetRequiredService<BundleGenerationService>());
|
||||
WebApplicationBuilder.Services.AddSingleton<BundleGenerationService>();
|
||||
WebApplicationBuilder.Services.AddSingleton(BundleService);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<DefaultItemExcludes>
|
||||
**\bin\**;**\obj\**;**\node_modules\**;**\Styles\*.json
|
||||
</DefaultItemExcludes>
|
||||
<StaticWebAssetsEnabled>True</StaticWebAssetsEnabled>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -40,11 +41,6 @@
|
||||
<PackagePath>src</PackagePath>
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="**\*.razor" Exclude="storage\**\*;bin\**\*;obj\**\*">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>src</PackagePath>
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Styles\**\*" Exclude="storage\**\*;bin\**\*;obj\**\*;Styles\node_modules\**\*">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>styles</PackagePath>
|
||||
@@ -72,10 +68,4 @@
|
||||
<ProjectReference Include="..\Moonlight.Shared\Moonlight.Shared.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Helpers\"/>
|
||||
<Folder Include="Styles\"/>
|
||||
<Folder Include="wwwroot\css\"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
npx tailwindcss -i style.css -o ../wwwroot/css/core.min.css --watch
|
||||
@@ -1,2 +0,0 @@
|
||||
#! /bin/bash
|
||||
npx tailwindcss -i style.css -o ../wwwroot/css/core.min.css --watch
|
||||
26
Moonlight.Client/Styles/exports.css
Normal file
26
Moonlight.Client/Styles/exports.css
Normal file
@@ -0,0 +1,26 @@
|
||||
@import "./additions/fonts.css";
|
||||
@import "./additions/theme.css" layer(theme);
|
||||
|
||||
/* @import "./additions/theme.white.css"; */
|
||||
|
||||
@import "./additions/buttons.css" layer(components);
|
||||
@import "./additions/cards.css" layer(components);
|
||||
@import "./additions/forms.css" layer(utilities);
|
||||
@import "./additions/progress.css" layer(components);
|
||||
@import "./additions/scrollbar.css" layer(components);
|
||||
@import "./additions/loaders.css" layer(components);
|
||||
@import "./additions/tabs.css" layer(components);
|
||||
|
||||
@source "./mappings/*.map";
|
||||
|
||||
#blazor-error-ui {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#blazor-loader-label:after {
|
||||
content: var(--blazor-load-percentage-text, "Loading");
|
||||
}
|
||||
|
||||
#blazor-loader-progress {
|
||||
width: var(--blazor-load-percentage, 0%);
|
||||
}
|
||||
28
Moonlight.Client/Styles/package-lock.json
generated
28
Moonlight.Client/Styles/package-lock.json
generated
@@ -7,7 +7,8 @@
|
||||
"dependencies": {
|
||||
"@tailwindcss/cli": "^4.1.4",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"tailwindcss": "^4.1.4"
|
||||
"tailwindcss": "^4.1.4",
|
||||
"xml2js": "^0.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher": {
|
||||
@@ -904,6 +905,11 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
|
||||
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg=="
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.5.tgz",
|
||||
@@ -927,6 +933,26 @@
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xml2js": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
|
||||
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
|
||||
"dependencies": {
|
||||
"sax": ">=0.6.0",
|
||||
"xmlbuilder": "~11.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
"dependencies": {
|
||||
"@tailwindcss/cli": "^4.1.4",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"tailwindcss": "^4.1.4"
|
||||
"tailwindcss": "^4.1.4",
|
||||
"xml2js": "^0.6.2"
|
||||
},
|
||||
"scripts": {
|
||||
"tailwind": "npx tailwindcss -i style.css -o ../wwwroot/css/style.min.css --watch",
|
||||
"pretailwind": "node resolveNuget.js ../Moonlight.Client.csproj"
|
||||
}
|
||||
}
|
||||
|
||||
1
Moonlight.Client/Styles/preTailwind.css
Normal file
1
Moonlight.Client/Styles/preTailwind.css
Normal file
@@ -0,0 +1 @@
|
||||
@import "./additions/fonts.css";
|
||||
80
Moonlight.Client/Styles/resolveNuget.js
Normal file
80
Moonlight.Client/Styles/resolveNuget.js
Normal file
@@ -0,0 +1,80 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const xml2js = require('xml2js');
|
||||
|
||||
// Helpers
|
||||
function getPackageRefs(csprojPath) {
|
||||
const xml = fs.readFileSync(csprojPath, 'utf8');
|
||||
const parser = new xml2js.Parser();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
parser.parseString(xml, (err, result) => {
|
||||
if (err) return reject(err);
|
||||
|
||||
const itemGroups = result.Project.ItemGroup || [];
|
||||
const refs = [];
|
||||
|
||||
for (const group of itemGroups) {
|
||||
const packages = group.PackageReference || [];
|
||||
for (const pkg of packages) {
|
||||
const name = pkg.$.Include;
|
||||
const version = pkg.$.Version || (pkg.Version && pkg.Version[0]);
|
||||
if (name && version) {
|
||||
refs.push({ name: name.toLowerCase(), version });
|
||||
}
|
||||
}
|
||||
}
|
||||
resolve(refs);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const csprojPath = process.argv[2];
|
||||
if (!csprojPath || !fs.existsSync(csprojPath)) {
|
||||
console.error('Usage: Missing csproj path');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const nugetPath = path.join(os.homedir(), '.nuget', 'packages');
|
||||
const moonlightDir = path.join(__dirname, 'node_modules', 'moonlight');
|
||||
fs.mkdirSync(moonlightDir, { recursive: true });
|
||||
|
||||
const refs = await getPackageRefs(csprojPath);
|
||||
|
||||
var outputCss = "";
|
||||
var preOutputCss = "";
|
||||
|
||||
for (const { name, version } of refs) {
|
||||
const packagePath = path.join(nugetPath, name, version);
|
||||
const exportsFile = path.join(packagePath, 'styles', 'exports.css');
|
||||
const preTailwindFile = path.join(packagePath, 'styles', 'preTailwind.css');
|
||||
const sourceFolder = path.join(packagePath, 'src');
|
||||
|
||||
const rel = (p) => p.replace(/\\/g, '/');
|
||||
|
||||
if (fs.existsSync(exportsFile)) {
|
||||
outputCss += `@import "${rel(exportsFile)}";\n`;
|
||||
}
|
||||
|
||||
if (fs.existsSync(preTailwindFile)) {
|
||||
preOutputCss += `@import "${rel(preTailwindFile)}";\n`;
|
||||
}
|
||||
|
||||
if (fs.existsSync(sourceFolder)) {
|
||||
outputCss += `@source "${rel(path.join(sourceFolder, "**", "*.razor"))}";\n`;
|
||||
outputCss += `@source "${rel(path.join(sourceFolder, "**", "*.cs"))}";\n`;
|
||||
outputCss += `@source "${rel(path.join(sourceFolder, "**", "*.html"))}";\n`;
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.join(moonlightDir, 'nuget.css'), outputCss);
|
||||
fs.writeFileSync(path.join(moonlightDir, 'preTailwind.nuget.css'), preOutputCss);
|
||||
console.log(`Generated nuget.css in ${moonlightDir}`);
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,32 +1,14 @@
|
||||
@import "./additions/fonts.css";
|
||||
@import "./preTailwind.css";
|
||||
@import "moonlight/preTailwind.nuget.css";
|
||||
|
||||
@import "tailwindcss";
|
||||
@import "./additions/theme.css" layer(theme);
|
||||
|
||||
/* @import "./additions/theme.white.css"; */
|
||||
|
||||
@import "./additions/buttons.css" layer(components);
|
||||
@import "./additions/cards.css" layer(components);
|
||||
@import "./additions/forms.css" layer(utilities);
|
||||
@import "./additions/progress.css" layer(components);
|
||||
@import "./additions/scrollbar.css" layer(components);
|
||||
@import "./additions/loaders.css" layer(components);
|
||||
@import "./additions/tabs.css" layer(components);
|
||||
@import "./exports.css";
|
||||
@import "moonlight/nuget.css";
|
||||
|
||||
@plugin "@tailwindcss/forms";
|
||||
|
||||
@source "../**/*.razor";
|
||||
@source "../**/*.cs";
|
||||
@source "../**/*.html";
|
||||
@source "./mappings/*.map";
|
||||
|
||||
#blazor-error-ui {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#blazor-loader-label:after {
|
||||
content: var(--blazor-load-percentage-text, "Loading");
|
||||
}
|
||||
|
||||
#blazor-loader-progress {
|
||||
width: var(--blazor-load-percentage, 0%);
|
||||
}
|
||||
@source "./mappings/*.map";
|
||||
@@ -6,10 +6,10 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Moonlight.Client</title>
|
||||
<base href="/" />
|
||||
<link rel="stylesheet" href="/css/bundle.css" />
|
||||
<link rel="stylesheet" href="/css/style.min.css" />
|
||||
<link href="manifest.webmanifest" rel="manifest" />
|
||||
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
|
||||
<link rel="apple-touch-icon" sizes="512x512" href="/img/icon-512.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="/img/icon-192.png" />
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-950 text-white font-inter h-full">
|
||||
|
||||
60
Resources/Scripts/Functions/ContentFunctions.cs
Normal file
60
Resources/Scripts/Functions/ContentFunctions.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Scripts.Functions;
|
||||
|
||||
public static class ContentFunctions
|
||||
{
|
||||
public static async Task Run(string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Console.WriteLine("Please provide the path to a nuget file and at least one regex expression");
|
||||
return;
|
||||
}
|
||||
|
||||
var nugetPath = args[0];
|
||||
|
||||
var regexs = args
|
||||
.Skip(1)
|
||||
.Select(x => new Regex(x))
|
||||
.ToArray();
|
||||
|
||||
Console.WriteLine(string.Join(", ", args
|
||||
.Skip(1)
|
||||
.Select(x => new Regex(x))));
|
||||
|
||||
if (!File.Exists(nugetPath))
|
||||
{
|
||||
Console.WriteLine("The provided file does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("Modding nuget package...");
|
||||
using var zipFile = ZipFile.Open(nugetPath, ZipArchiveMode.Update);
|
||||
|
||||
foreach (var zipArchiveEntry in zipFile.Entries)
|
||||
{
|
||||
Console.WriteLine(zipArchiveEntry.FullName);
|
||||
}
|
||||
|
||||
Console.WriteLine("Searching for files to remove");
|
||||
var files = zipFile.Entries
|
||||
.Where(x => x.FullName.Trim('/').StartsWith("content"))
|
||||
.Where(x =>
|
||||
{
|
||||
var name = x.FullName
|
||||
.Replace("contentFiles/", "")
|
||||
.Replace("content/", "");
|
||||
|
||||
Console.WriteLine(name);
|
||||
|
||||
return regexs.Any(y => y.IsMatch(name));
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
Console.WriteLine($"Found {files.Length} file(s) to remove");
|
||||
foreach (var file in files)
|
||||
file.Delete();
|
||||
}
|
||||
}
|
||||
47
Resources/Scripts/Functions/SrcFunctions.cs
Normal file
47
Resources/Scripts/Functions/SrcFunctions.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace Scripts.Functions;
|
||||
|
||||
public static class SrcFunctions
|
||||
{
|
||||
public static async Task Run(string[] args)
|
||||
{
|
||||
if (args.Length != 3)
|
||||
{
|
||||
Console.WriteLine("Please provide the path to a nuget file, a search pattern and a path");
|
||||
return;
|
||||
}
|
||||
|
||||
var nugetPath = args[0];
|
||||
var path = args[1];
|
||||
var pattern = args[2];
|
||||
|
||||
if (!File.Exists(nugetPath))
|
||||
{
|
||||
Console.WriteLine("The provided file does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("Modding nuget package...");
|
||||
using var zipFile = ZipFile.Open(nugetPath, ZipArchiveMode.Update);
|
||||
|
||||
var filesToAdd = Directory.GetFiles(path, pattern, SearchOption.AllDirectories);
|
||||
|
||||
foreach (var file in filesToAdd)
|
||||
{
|
||||
var name = file.Replace(path, "").Replace("\\", "/");
|
||||
|
||||
Console.WriteLine($"{file} => /src/{name}");
|
||||
|
||||
var entry = zipFile.CreateEntry($"src/{name}");
|
||||
await using var entryStream = entry.Open();
|
||||
|
||||
await using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
await fs.CopyToAsync(entryStream);
|
||||
fs.Close();
|
||||
|
||||
await entryStream.FlushAsync();
|
||||
entryStream.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
93
Resources/Scripts/Functions/StaticWebAssetsFunctions.cs
Normal file
93
Resources/Scripts/Functions/StaticWebAssetsFunctions.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Scripts.Functions;
|
||||
|
||||
public static class StaticWebAssetsFunctions
|
||||
{
|
||||
public static async Task Run(string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Console.WriteLine("Please provide the path to a nuget file and at least one regex expression");
|
||||
return;
|
||||
}
|
||||
|
||||
var nugetPath = args[0];
|
||||
|
||||
var regexs = args
|
||||
.Skip(1)
|
||||
.Select(x => new Regex(x))
|
||||
.ToArray();
|
||||
|
||||
if (!File.Exists(nugetPath))
|
||||
{
|
||||
Console.WriteLine("The provided file does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("Modding nuget package...");
|
||||
using var zipFile = ZipFile.Open(nugetPath, ZipArchiveMode.Update);
|
||||
|
||||
Console.WriteLine("Searching for files to remove");
|
||||
var files = zipFile.Entries
|
||||
.Where(x => x.FullName.Trim('/').StartsWith("staticwebassets"))
|
||||
.Where(x =>
|
||||
{
|
||||
var name = x.FullName.Replace("staticwebassets/", "");
|
||||
|
||||
return regexs.Any(y => y.IsMatch(name));
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
Console.WriteLine($"Found {files.Length} file(s) to remove");
|
||||
foreach (var file in files)
|
||||
file.Delete();
|
||||
|
||||
Console.WriteLine("Modifying static web assets build target");
|
||||
var oldBuildTargetEntry = zipFile
|
||||
.Entries
|
||||
.FirstOrDefault(x => x.FullName == "build/Microsoft.AspNetCore.StaticWebAssets.props");
|
||||
|
||||
if (oldBuildTargetEntry == null)
|
||||
{
|
||||
Console.WriteLine("Build target file not found in nuget packages");
|
||||
return;
|
||||
}
|
||||
|
||||
await using var oldBuildTargetStream = oldBuildTargetEntry.Open();
|
||||
|
||||
var contentXml = await XDocument.LoadAsync(
|
||||
oldBuildTargetStream,
|
||||
LoadOptions.None,
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
oldBuildTargetStream.Close();
|
||||
oldBuildTargetEntry.Delete();
|
||||
|
||||
var assetRefsToRemove = contentXml
|
||||
.Descendants("StaticWebAsset")
|
||||
.Where(asset =>
|
||||
{
|
||||
var element = asset.Element("RelativePath");
|
||||
|
||||
if (element == null)
|
||||
return false;
|
||||
|
||||
return regexs.Any(y => y.IsMatch(element.Value));
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
foreach (var asset in assetRefsToRemove)
|
||||
asset.Remove();
|
||||
|
||||
var newBuildTargetEntry = zipFile.CreateEntry("build/Microsoft.AspNetCore.StaticWebAssets.props");
|
||||
await using var newBuildTargetStream = newBuildTargetEntry.Open();
|
||||
|
||||
await contentXml.SaveAsync(newBuildTargetStream, SaveOptions.None, CancellationToken.None);
|
||||
|
||||
newBuildTargetStream.Close();
|
||||
}
|
||||
}
|
||||
26
Resources/Scripts/Program.cs
Normal file
26
Resources/Scripts/Program.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Scripts.Functions;
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
Console.WriteLine("You need to specify a module to run");
|
||||
return;
|
||||
}
|
||||
|
||||
var module = args[0];
|
||||
var moduleArgs = args.Skip(1).ToArray();
|
||||
|
||||
switch (module)
|
||||
{
|
||||
case "staticWebAssets":
|
||||
await StaticWebAssetsFunctions.Run(moduleArgs);
|
||||
break;
|
||||
case "content":
|
||||
await ContentFunctions.Run(moduleArgs);
|
||||
break;
|
||||
case "src":
|
||||
await SrcFunctions.Run(moduleArgs);
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine($"No module named {module} found");
|
||||
break;
|
||||
}
|
||||
10
Resources/Scripts/Scripts.csproj
Normal file
10
Resources/Scripts/Scripts.csproj
Normal file
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,97 +0,0 @@
|
||||
# Set strict mode to stop on errors
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Ensure the script is running in the main directory
|
||||
Write-Host "Building nuget packages"
|
||||
|
||||
Write-Host "Searching & building project files"
|
||||
# Find all .csproj files recursively
|
||||
$projectFiles = Get-ChildItem -Recurse -Filter "*.csproj"
|
||||
|
||||
foreach ($project in $projectFiles) {
|
||||
# Extract project name (without extension)
|
||||
$projectName = $project.BaseName
|
||||
|
||||
# Extract version from the .csproj file
|
||||
$projectVersion = Select-String -Path $project.FullName -Pattern "<Version>(.*?)</Version>" | ForEach-Object {
|
||||
$_.Matches.Groups[1].Value
|
||||
}
|
||||
|
||||
if (-not $projectVersion) {
|
||||
Write-Host "No <Version> tag found in $($project.FullName), skipping."
|
||||
continue
|
||||
}
|
||||
|
||||
# Build and pack the project
|
||||
$projectPath = $project.DirectoryName
|
||||
$pwd = (Get-Location).Path
|
||||
Push-Location $projectPath
|
||||
dotnet build --configuration Release
|
||||
dotnet pack --configuration Release --output "$pwd\nupkgs"
|
||||
Pop-Location
|
||||
|
||||
# Modifying the NuGet package
|
||||
Write-Host "Modding nuget package"
|
||||
$nugetPackage = Get-ChildItem "$pwd\nupkgs" -Filter "*.nupkg" | Select-Object -First 1
|
||||
|
||||
# Rename .nupkg to .zip
|
||||
$zipPackage = "$($nugetPackage.FullName).zip"
|
||||
Rename-Item -Path $nugetPackage.FullName -NewName $zipPackage
|
||||
|
||||
Expand-Archive -Path $zipPackage -DestinationPath "$pwd\nupkgs\mod" -Force
|
||||
|
||||
if ($projectName -eq "Moonlight.ApiServer") {
|
||||
Remove-Item "$pwd\nupkgs\mod\content" -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item "$pwd\nupkgs\mod\contentFiles" -Recurse -Force -ErrorAction SilentlyContinue
|
||||
|
||||
$xmlFilePath = "$pwd\nupkgs\mod\$projectName.nuspec"
|
||||
$xmlContent = Get-Content -Path $xmlFilePath -Raw
|
||||
$regexPattern = '<contentFiles\b[^>]*>[\s\S]*?<\/contentFiles>'
|
||||
|
||||
$updatedContent = [System.Text.RegularExpressions.Regex]::Replace(
|
||||
$xmlContent,
|
||||
$regexPattern,
|
||||
"",
|
||||
[System.Text.RegularExpressions.RegexOptions]::IgnoreCase
|
||||
)
|
||||
|
||||
Set-Content -Path $xmlFilePath -Value $updatedContent -Encoding UTF8
|
||||
}
|
||||
|
||||
if ($projectName -eq "Moonlight.Client") {
|
||||
Remove-Item "$pwd\nupkgs\mod\staticwebassets\_framework" -Recurse -Force
|
||||
|
||||
$xmlFilePath = "$pwd\nupkgs\mod\build\Microsoft.AspNetCore.StaticWebAssets.props"
|
||||
$xmlContent = Get-Content -Path $xmlFilePath -Raw
|
||||
|
||||
$regexPattern = '<StaticWebAsset\b[^>]*>(?:(?!<\/StaticWebAsset>).)*?<RelativePath>_framework/blazor\.webassembly\.js(?:\.gz)?<\/RelativePath>.*?<\/StaticWebAsset>'
|
||||
|
||||
$updatedContent = [System.Text.RegularExpressions.Regex]::Replace(
|
||||
$xmlContent,
|
||||
$regexPattern,
|
||||
"",
|
||||
[System.Text.RegularExpressions.RegexOptions]::Singleline
|
||||
)
|
||||
|
||||
Set-Content -Path $xmlFilePath -Value $updatedContent -Encoding UTF8
|
||||
}
|
||||
|
||||
# Repack the modified NuGet package
|
||||
Write-Host "Repacking nuget package"
|
||||
Remove-Item $zipPackage
|
||||
Push-Location "$pwd\nupkgs\mod"
|
||||
Compress-Archive -Path * -DestinationPath $zipPackage -Force
|
||||
Pop-Location
|
||||
|
||||
# Rename .zip back to .nupkg
|
||||
Rename-Item -Path $zipPackage -NewName $nugetPackage.FullName
|
||||
|
||||
# Move the final package to the output directory
|
||||
$finalDir = "$pwd\finalPackages"
|
||||
New-Item -ItemType Directory -Path $finalDir -Force | Out-Null
|
||||
Move-Item -Path $nugetPackage.FullName -Destination $finalDir
|
||||
|
||||
# Cleanup
|
||||
Write-Host "Cleaning up"
|
||||
Remove-Item "$pwd\nupkgs\mod" -Recurse -Force
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Note: Run in main directory, i.e. where the Moonlight.sln is
|
||||
|
||||
echo "Building nuget packages"
|
||||
|
||||
echo "Searching & building project files"
|
||||
project_files=$(find . -name "*.csproj")
|
||||
|
||||
for project in $project_files; do
|
||||
# Extract project name
|
||||
project_name=$(basename "$project" .csproj)
|
||||
|
||||
# Extract version
|
||||
project_version=$(grep -oPm1 "(?<=<Version>)[^<]+" "$project")
|
||||
if [ -z "$project_version" ]; then
|
||||
echo "No <Version> tag found in $project, skipping."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Building nuget package
|
||||
pwd=$(pwd)
|
||||
project_path=$(dirname $project)
|
||||
(cd $project_path; dotnet build --configuration Release; dotnet pack --configuration Release --output $pwd/nupkgs)
|
||||
|
||||
# Mod nuget package
|
||||
echo "Modding nuget package"
|
||||
nugetPackage=$(find $pwd/nupkgs -name "*.nupkg")
|
||||
|
||||
unzip -o $nugetPackage -d $pwd/nupkgs/mod
|
||||
|
||||
if [ "$project_name" = "Moonlight.ApiServer" ]; then
|
||||
rm -r $pwd/nupkgs/mod/content
|
||||
rm -r $pwd/nupkgs/mod/contentFiles
|
||||
|
||||
sed -i "/<contentFiles>/,/<\/contentFiles>/d" $pwd/nupkgs/mod/Moonlight.ApiServer.nuspec
|
||||
fi
|
||||
|
||||
if [ "$project_name" = "Moonlight.Client" ]; then
|
||||
rm -r $pwd/nupkgs/mod/staticwebassets/_framework
|
||||
|
||||
sed -i '/<StaticWebAsset Include=.*blazor.webassembly.js.gz.*>/,/<\/StaticWebAsset>/d' $pwd/nupkgs/mod/build/Microsoft.AspNetCore.StaticWebAssets.props
|
||||
sed -i '/<StaticWebAsset Include=.*blazor.webassembly.js.*>/,/<\/StaticWebAsset>/d' $pwd/nupkgs/mod/build/Microsoft.AspNetCore.StaticWebAssets.props
|
||||
|
||||
fi
|
||||
|
||||
echo "Repacking nuget package"
|
||||
rm $nugetPackage
|
||||
(cd nupkgs/mod/; zip -r -o $nugetPackage *)
|
||||
|
||||
mkdir -p $pwd/finalPackages/
|
||||
|
||||
mv $nugetPackage $pwd/finalPackages/
|
||||
|
||||
echo "Cleaning up"
|
||||
rm -r $pwd/nupkgs/mod
|
||||
done
|
||||
25
Resources/Scripts/generateNuget.sh
Normal file
25
Resources/Scripts/generateNuget.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
# We are building the packages in the debug mode because they are meant for development
|
||||
# purposes only. When build for production, release builds will be used ofc
|
||||
|
||||
set -e
|
||||
|
||||
echo "Creating nuget packages"
|
||||
mkdir -p finalPackages
|
||||
|
||||
echo "+ ApiServer Server"
|
||||
dotnet build -c Debug Moonlight.ApiServer
|
||||
dotnet pack -c Debug Moonlight.ApiServer --output finalPackages/
|
||||
dotnet run --project Resources/Scripts/Scripts.csproj content finalPackages/Moonlight.ApiServer.2.1.0.nupkg ".*"
|
||||
|
||||
echo "+ Client"
|
||||
dotnet build -c Debug Moonlight.Client
|
||||
dotnet pack -c Debug Moonlight.Client --output finalPackages/
|
||||
dotnet run --project Resources/Scripts/Scripts.csproj staticWebAssets finalPackages/Moonlight.Client.2.1.0.nupkg "_framework\/.*" style.min.css
|
||||
dotnet run --project Resources/Scripts/Scripts.csproj src finalPackages/Moonlight.Client.2.1.0.nupkg Moonlight.Client/ *.razor
|
||||
dotnet run --project Resources/Scripts/Scripts.csproj src finalPackages/Moonlight.Client.2.1.0.nupkg Moonlight.Client/ wwwroot/*.html
|
||||
|
||||
echo "+ Shared library"
|
||||
dotnet build -c Debug Moonlight.Shared
|
||||
dotnet pack -c Debug Moonlight.Shared --output finalPackages/
|
||||
@@ -1,66 +0,0 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
// Handle arguments
|
||||
if (Args.Count != 1)
|
||||
{
|
||||
Console.WriteLine("You need to provide the path to a nuget file as a parameter");
|
||||
return;
|
||||
}
|
||||
|
||||
var nugetPath = Args[0];
|
||||
|
||||
// Check if file exists
|
||||
if (!File.Exists(nugetPath))
|
||||
{
|
||||
Console.WriteLine("The provided file does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
// Open file to modify
|
||||
Console.WriteLine($"Modding nuget package: {nugetPath}");
|
||||
var zipFile = ZipFile.Open(nugetPath, ZipArchiveMode.Update, Encoding.UTF8);
|
||||
|
||||
// First we want to remove the framework files
|
||||
Console.WriteLine("Removing framework files");
|
||||
|
||||
var frameworkEntries = zipFile.Entries
|
||||
.Where(x => x.FullName.Contains("staticwebassets/_framework"))
|
||||
.ToArray();
|
||||
|
||||
foreach (var frameworkEntry in frameworkEntries)
|
||||
frameworkEntry.Delete();
|
||||
|
||||
// Then we want to modify the build targets for static web assets
|
||||
var oldBuildTarget = zipFile.Entries
|
||||
.First(x => x.FullName == "build/Microsoft.AspNetCore.StaticWebAssets.props");
|
||||
|
||||
// Load old content
|
||||
var oldContentStream = oldBuildTarget.Open();
|
||||
|
||||
// Parse xml and remove framework references
|
||||
Console.WriteLine("Removing framework web asset references");
|
||||
|
||||
var contentXml = XDocument.Load(oldContentStream);
|
||||
oldContentStream.Close();
|
||||
oldContentStream.Dispose();
|
||||
oldBuildTarget.Delete();
|
||||
|
||||
var assetsToRemove = contentXml
|
||||
.Descendants("StaticWebAsset")
|
||||
.Where(asset =>
|
||||
asset.Element("RelativePath")?.Value.Contains("_framework") == true)
|
||||
.ToArray();
|
||||
|
||||
foreach (var asset in assetsToRemove)
|
||||
asset.Remove();
|
||||
|
||||
var newBuildTarget = zipFile.CreateEntry("build/Microsoft.AspNetCore.StaticWebAssets.props");
|
||||
var newContentStream = newBuildTarget.Open();
|
||||
contentXml.Save(newContentStream);
|
||||
|
||||
await newContentStream.FlushAsync();
|
||||
newContentStream.Close();
|
||||
|
||||
zipFile.Dispose();
|
||||
@@ -1,15 +0,0 @@
|
||||
# This script requires a NuGet override folder at %userprofile%\NugetOverride
|
||||
|
||||
# Clear old build cache
|
||||
Remove-Item -Recurse -Force nupkgs, finalPackages
|
||||
|
||||
# Build and replace NuGet packages
|
||||
& "Resources\Scripts\buildNuget.ps1"
|
||||
Copy-Item -Path finalPackages\* -Destination $env:userprofile\NugetOverride -Force
|
||||
|
||||
# Clean package cache
|
||||
Remove-Item -Recurse -Force $env:userprofile\.nuget\packages\moonlight.apiserver
|
||||
Remove-Item -Recurse -Force $env:userprofile\.nuget\packages\moonlight.shared
|
||||
Remove-Item -Recurse -Force $env:userprofile\.nuget\packages\moonlight.client
|
||||
|
||||
Write-Output "Done :>"
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script required a nuget override folder at ~/NugetOverride
|
||||
|
||||
# Clear old build cache
|
||||
rm -rf nupkgs/ finalPackages/
|
||||
|
||||
# Build and replace nuget packages
|
||||
bash Resources/Scripts/buildNuget.sh
|
||||
cp finalPackages/* ~/NugetOverride/
|
||||
|
||||
# Clean package cache
|
||||
rm -rf ~/.nuget/packages/moonlight.apiserver/
|
||||
rm -rf ~/.nuget/packages/moonlight.shared/
|
||||
rm -rf ~/.nuget/packages/moonlight.client/
|
||||
|
||||
echo "Done :>"
|
||||
7
Resources/Scripts/prepareNugetOverride.sh
Normal file
7
Resources/Scripts/prepareNugetOverride.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
bash Resources/Scripts/generateNuget.sh
|
||||
|
||||
echo "+ Copying to nuget override"
|
||||
cp finalPackages/*.nupkg ~/NugetOverride/
|
||||
rm -r ~/.nuget/packages/moonlight.*
|
||||
Reference in New Issue
Block a user