Generated based on analysis of AspNetCore.SassCompiler, BundlerMinifier, WebCompiler, and Microsoft NuGet best practices.
This document outlines improvements to elevate DotnetDevKR.TailwindCSS to production-quality standards based on industry best practices and comparable NuGet packages.
Status: MISSING Impact: Package appears incomplete on NuGet.org, reduced discoverability
Requirements:
- Create 128x128 PNG icon with transparent background
- Embed in package (not external URL)
- Add to root of repository as
icon.png
Implementation:
<PackageIcon>icon.png</PackageIcon>
<ItemGroup>
<None Include="..\icon.png" Pack="true" PackagePath="\" />
</ItemGroup>Status: MISSING Impact: NuGet.org shows blank description
Recommended:
<Description>Automatic TailwindCSS compilation for .NET projects without Node.js dependency. Includes cross-platform standalone CLI with MSBuild integration supporting Blazor, ASP.NET Core, and Razor projects.</Description>Current: <PackageTags>TailwindCSS</PackageTags>
Impact: Poor search discoverability
Recommended:
<PackageTags>tailwindcss;tailwind;css;msbuild;aspnetcore;blazor;compiler;build;frontend;no-nodejs;standalone</PackageTags><PackageProjectUrl>https://github.com/dotnetdev-kr/DotnetDevKR.TailwindCSS</PackageProjectUrl>
<Copyright>Copyright (c) 2024-2025 DotnetDevKR</Copyright>
<PackageReleaseNotes>See https://github.com/dotnetdev-kr/DotnetDevKR.TailwindCSS/releases</PackageReleaseNotes>
<DevelopmentDependency>true</DevelopmentDependency>Status: MISSING Pattern from: BundlerMinifier
Users should be able to disable TailwindCSS compilation:
<Target Name="RunTailwindCSSTask"
BeforeTargets="Build"
Condition="'$(RunTailwindCSS)' != 'false' AND '$(DesignTimeBuild)' != 'true'"
Inputs="@(Watch);$(InputFilename)"
Outputs="$(OutputFilename)">Status: MISSING Impact: Build fails silently if user forgets configuration
<PropertyGroup>
<InputFilename Condition="'$(InputFilename)' == ''">$(ProjectDir)tailwind.css</InputFilename>
<IsMinify Condition="'$(IsMinify)' == ''">false</IsMinify>
<DebugMode Condition="'$(DebugMode)' == ''">false</DebugMode>
</PropertyGroup>Current: Watches **\*.cs, **\*.cshtml, **\*.html, **\*.razor
Issue: C# changes trigger unnecessary recompilation
Recommended:
<ItemGroup>
<Watch Include="**\*.razor" AlwaysRebuild="true" />
<Watch Include="**\*.html" AlwaysRebuild="true" />
<Watch Include="**\*.cshtml" AlwaysRebuild="true" />
<Watch Include="tailwind.config.js" AlwaysRebuild="true" Condition="Exists('$(ProjectDir)tailwind.config.js')" />
<Watch Include="**\*.css" Exclude="$(OutputFilename)" AlwaysRebuild="true" />
</ItemGroup>Remove **\*.cs from watch list.
Current: No validation Impact: Silent failures, confusing error messages
public override bool Execute()
{
if (string.IsNullOrEmpty(OutputFilename))
{
Log.LogError("TAILWIND001: OutputFilename property is required. Add <OutputFilename> to your project configuration.");
return false;
}
if (!string.IsNullOrEmpty(InputFilename) && !File.Exists(InputFilename))
{
Log.LogWarning("TAILWIND002: Input file not found: {0}. TailwindCSS will scan for utility classes only.", InputFilename);
}
// Change from LogWarning to LogMessage
Log.LogMessage(MessageImportance.High, $"Compiling TailwindCSS from '{InputFilename ?? "(auto)"}' to '{OutputFilename}'");
// ... rest
}Current: Generic exceptions, no timeout Impact: Hung builds, poor error messages
public async Task CompileAsync(...)
{
using var process = CreateTailwindCSSProcess(...);
using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
try
{
process.Start();
// ... existing stream handling
await process.WaitForExitAsync(cts.Token);
}
catch (OperationCanceledException)
{
process.Kill(entireProcessTree: true);
throw new TailwindCSSCompilationException("TailwindCSS compilation timed out after 5 minutes");
}
if (process.ExitCode != 0)
{
throw new TailwindCSSCompilationException(
$"TailwindCSS exited with code {process.ExitCode}",
errorOutput.ToString());
}
}public class TailwindCSSCompilationException : Exception
{
public string? CompilerOutput { get; }
public int? ExitCode { get; }
public TailwindCSSCompilationException(string message, string? compilerOutput = null, int? exitCode = null)
: base(message)
{
CompilerOutput = compilerOutput;
ExitCode = exitCode;
}
}Current: v15.9.20 (2019) Recommended: v17.x (latest for .NET 6.0+)
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.8.3" /><ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
</ItemGroup>
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>Add /// <summary> comments to all public APIs:
TailwindCSSTaskclass and propertiesTailwindCSSCompiler.CompileAsync()methodTailwindCSSCompiler.GetExecutablePath()method
Allow users to pass additional CLI arguments:
<PropertyGroup>
<TailwindCSSArguments>--config ./custom-config.js</TailwindCSSArguments>
</PropertyGroup>Implementation in TailwindCSSCompiler.cs:
$"-i {InputFilename} -o {OutputFilename} --cwd {ProjectDir}" +
(isMinify ? " --minify" : "") +
(isDebug ? " --map" : "") +
(!string.IsNullOrEmpty(additionalArgs) ? $" {additionalArgs}" : "")Enable multiple TailwindCSS compilations per project:
<ItemGroup>
<TailwindCompilation Include="main">
<Input>$(ProjectDir)tailwind.css</Input>
<Output>wwwroot\css\app.css</Output>
</TailwindCompilation>
<TailwindCompilation Include="admin">
<Input>$(ProjectDir)admin\tailwind.css</Input>
<Output>wwwroot\admin\css\app.css</Output>
</TailwindCompilation>
</ItemGroup>- Create
icon.png(128x128, transparent background) - Add
<Description>property - Expand
<PackageTags> - Add
<PackageProjectUrl>,<Copyright>,<DevelopmentDependency>
- Add
RunTailwindCSSconditional execution - Add
DesignTimeBuildexclusion - Add property defaults in
.targets - Refine Watch patterns (remove
*.cs)
- Add validation in
TailwindCSSTask.Execute() - Change
LogWarningtoLogMessagefor normal output - Add timeout to process execution
- Create
TailwindCSSCompilationExceptionclass - Improve error messages with codes (TAILWIND001, etc.)
- Update
Microsoft.Build.Utilities.Coreto v17.x - Add Source Link
- Add XML documentation comments
- Enable symbol packages
- Add
TailwindCSSArgumentsproperty - Add multiple compilation support
- Add configuration file support (
tailwind.msbuild.json)
| Feature | Before | After | Status |
|---|---|---|---|
| Package Icon | None | 128x128 PNG | TODO |
| Package Tags | 1 tag | 11 tags | TODO |
| Description | None | Full description | TODO |
| Conditional Execution | None | RunTailwindCSS property |
TODO |
| Design-time Build Exclusion | None | DesignTimeBuild check |
TODO |
| Property Defaults | None | InputFilename, IsMinify, DebugMode | TODO |
| Input Validation | None | File existence, required props | TODO |
| Process Timeout | None | 5 minute timeout | TODO |
| Custom Exception | Generic Exception | TailwindCSSCompilationException | TODO |
| Error Codes | None | TAILWIND001, TAILWIND002 | TODO |
| MSBuild Package Version | 15.9.20 | 17.8.3 | TODO |
| Source Link | None | Microsoft.SourceLink.GitHub | TODO |
| Symbol Package | None | snupkg format | TODO |
- AspNetCore.SassCompiler - Primary inspiration
- BundlerMinifier - Conditional execution pattern
- NuGet Package Authoring Best Practices
- MSBuild Props and Targets in NuGet