Skip to content

Commit

Permalink
Added regex apttern support
Browse files Browse the repository at this point in the history
  • Loading branch information
zorgoz committed Apr 8, 2024
1 parent ca2bb51 commit ba50748
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 18 deletions.
5 changes: 2 additions & 3 deletions PreCompress.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
<Title>Precompressor for ASP.NET Core</Title>
<Authors>zorgoz</Authors>
<Company>zorgoz</Company>
<Description>Tool to be used to created precompressed content to be served by Kestler.
Created to work around wrong compressed content during publish.</Description>
<Description>Tool to be used to created precompressed content to be served by Kestler. Created to work around wrong compressed content during publish.</Description>
<Copyright>MIT</Copyright>
<RepositoryUrl>https://github.com/zorgoz/PreCompress</RepositoryUrl>
<PackageProjectUrl>https://github.com/zorgoz/PreCompress</PackageProjectUrl>
Expand All @@ -21,7 +20,7 @@ Created to work around wrong compressed content during publish.</Description>
<PackageTags>brotli, gzip, http compression, asp.net core, kestler </PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<EnableNETAnalyzers>False</EnableNETAnalyzers>
<Version>0.9.3</Version>
<Version>0.9.5</Version>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageId>zorgoz.$(AssemblyName)</PackageId>
</PropertyGroup>
Expand Down
77 changes: 67 additions & 10 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -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<FileInfo[]?>(
Expand Down Expand Up @@ -46,7 +45,6 @@ _ when File.Exists(x.pattern) =>
AllowMultipleArgumentsPerToken = true,
IsRequired = true,
};

fileOption.AddAlias("--input");

var bratliOption = new Option<bool>(name: "-nb", description: "Omit Brotli compressed version.", getDefaultValue: () => false);
Expand All @@ -55,35 +53,94 @@ _ when File.Exists(x.pattern) =>
var gzipOption = new Option<bool>(name: "-ng", description: "Omit GZip compressed version.", getDefaultValue: () => false);
gzipOption.AddAlias("--NoGzip");

var patternOption = new Option<string?>(
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<DirectoryInfo?>(name: "-d", description: "The output directory.");
destinationOption.AddAlias("--destination");

var rootCommand = new RootCommand("PreCompression tool for ASP.NET Core");
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<string> 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));
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;
}
36 changes: 31 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -9,6 +9,7 @@ This tool is intended to be used as a post-publish step to create the compressed
-i, --input <i> (REQUIRED) The file to compress. []
-nb, --NoBrotli Omit Brotli compressed version. [default: False]
-ng, --NoGzip Omit GZip compressed version. [default: False]
-p, --pattern <p> The regex pattern to use for files in a folder. [default: *.]
-d, --destination <d> The output directory.
```

Expand All @@ -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`
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.
```
<Target Name="PreCompress" AfterTargets="Publish">
<Exec Command="dotnet tool run precompress -i $(PublishDir)wwwroot\_framework\ -p \.(dll|js|json|wasm|blat)$"></Exec>
</Target>
```

## 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)

0 comments on commit ba50748

Please sign in to comment.