From ba507483ebfedfa54b5f208a6d52cd968154a335 Mon Sep 17 00:00:00 2001 From: zorgoz Date: Mon, 8 Apr 2024 11:55:34 +0200 Subject: [PATCH] Added regex apttern support --- PreCompress.csproj | 5 ++- Program.cs | 77 ++++++++++++++++++++++++++++++++++++++++------ README.md | 36 +++++++++++++++++++--- 3 files changed, 100 insertions(+), 18 deletions(-) diff --git a/PreCompress.csproj b/PreCompress.csproj index b4792cf..54f72fb 100644 --- a/PreCompress.csproj +++ b/PreCompress.csproj @@ -11,8 +11,7 @@ Precompressor for ASP.NET Core zorgoz zorgoz - Tool to be used to created precompressed content to be served by Kestler. -Created to work around wrong compressed content during publish. + Tool to be used to created precompressed content to be served by Kestler. Created to work around wrong compressed content during publish. MIT https://github.com/zorgoz/PreCompress https://github.com/zorgoz/PreCompress @@ -21,7 +20,7 @@ Created to work around wrong compressed content during publish. brotli, gzip, http compression, asp.net core, kestler MIT False - 0.9.3 + 0.9.5 README.md zorgoz.$(AssemblyName) diff --git a/Program.cs b/Program.cs index dfa3f04..88d2a1a 100644 --- a/Program.cs +++ b/Program.cs @@ -1,7 +1,6 @@ using System.CommandLine; using System.CommandLine.Parsing; -using System.Runtime.CompilerServices; -using System.Xml.Schema; +using System.Text.RegularExpressions; using PreCompress; var fileOption = new Option( @@ -46,7 +45,6 @@ _ when File.Exists(x.pattern) => AllowMultipleArgumentsPerToken = true, IsRequired = true, }; - fileOption.AddAlias("--input"); var bratliOption = new Option(name: "-nb", description: "Omit Brotli compressed version.", getDefaultValue: () => false); @@ -55,6 +53,25 @@ _ when File.Exists(x.pattern) => var gzipOption = new Option(name: "-ng", description: "Omit GZip compressed version.", getDefaultValue: () => false); gzipOption.AddAlias("--NoGzip"); +var patternOption = new Option( + name: "-p", + description: "Use regular expression pattern to filter files", + parseArgument: result => { + var res = result.Tokens.FirstOrDefault()?.Value; + + if (string.IsNullOrWhiteSpace(res)) return "*."; + + if(!IsValidRegexPattern(res)) + { + result.ErrorMessage = "Invalid pattern specified"; + return null; + } + + return res; + } + ); +patternOption.AddAlias("--pattern"); + var destinationOption = new Option(name: "-d", description: "The output directory."); destinationOption.AddAlias("--destination"); @@ -62,28 +79,68 @@ _ when File.Exists(x.pattern) => rootCommand.AddOption(fileOption); rootCommand.AddOption(bratliOption); rootCommand.AddOption(gzipOption); +rootCommand.AddOption(patternOption); rootCommand.AddOption(destinationOption); -rootCommand.SetHandler(ReCompress, fileOption, bratliOption, gzipOption, destinationOption); +rootCommand.SetHandler(ReCompress, fileOption, bratliOption, gzipOption, patternOption, destinationOption); + +rootCommand.AddValidator(result => +{ + if (result.GetValueForOption(destinationOption) is not null && result.GetValueForOption(fileOption)?.Length > 0) + { + result.ErrorMessage = "Destination can only be specified when a single file is being processed"; + } +}); return await rootCommand.InvokeAsync(args); -static async Task ReCompress(FileInfo[]? files, bool omitBratli, bool omitGZip, DirectoryInfo? destination) +static async Task ReCompress(FileInfo[]? files, bool omitBratli, bool omitGZip, string? pattern, DirectoryInfo? destination) { if(files is null) return; - foreach (var file in files) + var opts = RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled; + +#if NET8_0_OR_GREATER + opts |= RegexOptions.NonBacktracking; +#endif + + Predicate match = + string.IsNullOrWhiteSpace(pattern) ? + _ => true : + new Regex(pattern, opts).IsMatch; + + var list = files.Where(f => match(f.FullName)); + + var tasks = list.Select(async file => { try { - Console.WriteLine($"Processing {file.FullName}"); + Console.WriteLine($"Compressing {file.FullName}"); await Compression.CompressAsync(file, !omitBratli, !omitGZip, destination, default); } catch (Exception ex) { Console.Error.WriteLine($"Error processing {file.FullName}: {ex.Message}"); } - - } + }); + + await Task.WhenAll(tasks); + Console.WriteLine($"Compression finished"); } -static string? EmptyCoalesce(params string?[] values) => values.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x)); \ No newline at end of file +static string? EmptyCoalesce(params string?[] values) => values.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x)); + +static bool IsValidRegexPattern(string pattern, string testText = "", int maxMillisecondTimeOut = 10) +{ + if (string.IsNullOrEmpty(pattern)) return false; + try + { + Regex re = new Regex(pattern, RegexOptions.None, TimeSpan.FromMilliseconds(maxMillisecondTimeOut)); + re.IsMatch(testText); + } + catch + { + return false; + } + + return true; +} \ No newline at end of file diff --git a/README.md b/README.md index 4732962..f9ab8b2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PreCompress -Simple tool to crete pre-compressed version for static content files to be served by Kestler or IIS with ASP.NET Core. -The need arose from an issue with publishing, where the pre-compressed files had different (older) content ss the non-compressed versions. +Simple tool to create pre-compressed version for static content files to be served by Kestler or IIS with ASP.NET Core. +The need arose from an issue with publishing, where the pre-compressed files had different (older) content as the non-compressed versions. This tool is intended to be used as a post-publish step to create the compressed files. ## Usage @@ -9,6 +9,7 @@ This tool is intended to be used as a post-publish step to create the compressed -i, --input (REQUIRED) The file to compress. [] -nb, --NoBrotli Omit Brotli compressed version. [default: False] -ng, --NoGzip Omit GZip compressed version. [default: False] +-p, --pattern

The regex pattern to use for files in a folder. [default: *.] -d, --destination The output directory. ``` @@ -21,13 +22,38 @@ Destination directory should exist if specified. ### Examples: ``` PreCompress -i C:\temp\source\*.dll -d c:\temp\destination -PreCompress -i C:\temp\source +PreCompress -i C:\temp\source\*.dll -i C:\temp\source\*.json +PreCompress -i C:\temp\source\ -p *\.(dll|js.*)$ PreCompress -i source\file.dll PreCompress -i *.dll ``` ## Installation -The tool is available as a dotnet global tool. To install it follow the instructions on nuget: +The tool is available as a dotnet global or local tool. To install it follow the instructions on nuget: https://www.nuget.org/packages/zorgoz.PreCompress/ -`dotnet tool install --global zorgoz.PreCompress` \ No newline at end of file +Install as global tool with + +``` +dotnet tool install --global zorgoz.PreCompress` +``` + +or as local tool with (be aware to install in the correct path) + +``` +dotnet new tool-manifest +dotnet tool install zorgoz.PreCompress +``` + + +## Usage as a tool +After installing as tool, you can add this to you `.csproj` file as a post-publish step. +``` + + + +``` + +## Planned features +- [ ] Add support for recursive folder search (currently only one level deep), first without destination folder +- [ ] Add support for recursivity with destination folder (copy unmatching parts of the folder structure)