feat(nuget): ship Tailwind safelist for NuGet consumers, bump to 4.3.1#20
Merged
Conversation
…installation - Introduced `SafelistDriftTests` to ensure the safelist matches generated Tailwind classes from Razor components, preventing missing styles in NuGet packages. - Enhanced CI workflow to verify the safelist is correctly included in the NuGet package and contains essential Tailwind classes after installation. - Updated project references in the test project to include the `ShellUI.SafelistGenerator` for safelist generation.
- Introduced a new CI job to validate that the safelist file `shellui-classes.txt` is correctly included in the NuGet package after installation. - The job checks for the presence of the safelist and verifies it contains essential Tailwind classes, ensuring proper integration for consumers using the package.
- Introduced a new file `shellui-classes.txt` containing a comprehensive list of Tailwind CSS classes to be used in the project. - This addition ensures that essential styles are available for components, enhancing the UI consistency and functionality.
- Replaced hardcoded Tailwind CSS version with constants from TailwindConstants for better maintainability. - Updated the installation command to use dynamic versioning, ensuring compatibility with future updates. - Enhanced console output to reflect the installed version dynamically, improving user feedback during installation.
- Updated all references to Tailwind CSS from v4.1.18 to v4.3.1 in README.md and src/ShellUI.Components/README.md. - Adjusted installation instructions to reflect the new version and ensure compatibility with existing setups. - Enhanced clarity on the two installation paths for users, emphasizing the benefits of each approach.
- Added a using directive for ShellUI.Core. - Updated TailwindConfig to use TailwindConstants.Version instead of a hardcoded string for the Tailwind CSS version, improving maintainability.
- Introduced a new tool in `ShellUI.SafelistGenerator` to scan .razor files for Tailwind utility classes and generate a unique safelist. - Added `TailwindConstants` to centralize the Tailwind CSS versioning, ensuring consistency across the project. - This addition enhances the maintainability and usability of Tailwind CSS within the ShellUI framework.
- Introduced a new project `ShellUI.SafelistGenerator` within the `tools` folder to facilitate the generation of Tailwind CSS safelists. - Updated the solution file to include the new project, enhancing the overall tooling for Tailwind CSS integration in the ShellUI framework.
- Updated references to Tailwind CSS from v4.1.18 to v4.3.1 in COMPONENT_ROADMAP.md and PROJECT_STATUS.md. - Ensured consistency across documentation regarding the latest Tailwind CSS version, enhancing clarity for users.
- Updated the CI configuration to use a dedicated NuGet.config that restricts package sources to a local feed, preventing fallback to older versions from nuget.org. - Added checks to ensure the local feed exists and contains the expected package before proceeding with the installation. - Improved validation of the safelist file `shellui-classes.txt` to ensure it is correctly generated and contains essential Tailwind classes after the NuGet restore process.
- Introduced `ShellUI.Components.targets` to facilitate the automatic copying of the Tailwind safelist file `shellui-classes.txt` into the consumer's `wwwroot/` during the build process. - Updated the project file to include the new targets and safelist file, ensuring seamless integration for users leveraging Tailwind CSS within the ShellUI framework.
…oject build - Modified the CI configuration to build the project before checking for the presence of the `shellui-classes.txt` safelist file in the `wwwroot/` directory. - Added error handling to provide clearer feedback if the safelist file is not found, ensuring better diagnostics for users integrating Tailwind CSS.
- Added a new test to verify that the `build/ShellUI.Components.targets` file contains the same classes as the safelist, ensuring consistency for NuGet consumers. - Introduced a constant for the regeneration command to improve clarity and maintainability in error messages. - Updated existing tests to utilize the new constant for better readability.
- Updated `ShellUI.Components.targets` to embed the Tailwind safelist inline, eliminating the need for a separate `shellui-classes.txt` file. - Modified project file to reflect the changes, ensuring seamless integration for consumers without dependency on external files. - Enhanced documentation within the targets file to clarify the auto-generation process and usage.
- Incremented the Tailwind CSS version from 4.3.1 to 4.3.2 in the TailwindConstants class to reflect the latest release, ensuring consistency across the project.
- Updated the `ShellUI.SafelistGenerator` to generate both a sorted list of Tailwind utility classes and a build targets file for NuGet integration. - Modified command-line arguments to accept separate output paths for the text file and targets file. - Improved documentation within the code to clarify usage and the purpose of generated files.
- Introduced a new test in `SafelistDriftTests` to validate that the generated `ShellUI.Components.targets` file is well-formed XML, ensuring compatibility with strict MSBuild parsers. - Updated the `ShellUI.SafelistGenerator` to include comments that avoid invalid XML sequences, enhancing robustness in generated output. - Improved documentation within the targets file to clarify the auto-generation process and potential pitfalls.
- Updated comments in `ShellUI.Components.targets` to enhance clarity regarding the auto-generation process and the embedding of the Tailwind safelist. - Simplified language to improve readability and understanding for future maintainers.
…iance - Enhanced comments in `Program.cs` to clarify the auto-generation process and avoid invalid XML sequences, specifically the `--` sequence in XML comments. - Simplified language for better readability and understanding, ensuring future maintainers can easily grasp the purpose and usage of the generated targets file.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR aims to make the NuGet-only consumption path for ShellUI.Components produce correctly styled components by shipping a Tailwind safelist that can be scanned during a consumer build, while also centralizing/bumping the Tailwind version and adding CI/test coverage to prevent safelist drift.
Changes:
- Adds a SafelistGenerator tool and commits generated artifacts (
shellui-classes.txtand an auto-imported.targets) to support Tailwind scanning for NuGet consumers. - Centralizes the Tailwind version into
TailwindConstantsand wires CLI/config defaults/docs to that single source of truth. - Adds drift tests + a CI NuGet-smoke step to ensure the safelist continues to ship and stays in sync.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/ShellUI.SafelistGenerator/ShellUI.SafelistGenerator.csproj | New console tool project for generating the safelist + targets artifact. |
| tools/ShellUI.SafelistGenerator/Program.cs | Implements safelist extraction + .targets generation. |
| src/ShellUI.Core/TailwindConstants.cs | Introduces centralized Tailwind version constants. |
| src/ShellUI.Core/Models/ShellUIConfig.cs | Uses TailwindConstants.Version as the default Tailwind version. |
| src/ShellUI.Components/wwwroot/shellui-classes.txt | Committed safelist text file generated from component sources. |
| src/ShellUI.Components/ShellUI.Components.csproj | Packs build/ShellUI.Components.targets into the NuGet package. |
| src/ShellUI.Components/README.md | Documents the new “NuGet + existing Tailwind” install path. |
| src/ShellUI.Components/build/ShellUI.Components.targets | Auto-imported MSBuild target that writes the safelist into a consumer’s wwwroot/ at build time. |
| src/ShellUI.CLI/Services/TailwindDownloader.cs | Switches Tailwind download tag to TailwindConstants.GitHubTag. |
| src/ShellUI.CLI/Services/InitService.cs | Switches npm install/version defaults to TailwindConstants. |
| ShellUI.Tests/ShellUI.Tests.csproj | Adds test-time reference to the safelist generator tool. |
| ShellUI.Tests/SafelistDriftTests.cs | Adds drift tests for safelist + targets XML validity and sync. |
| ShellUI.slnx | Adds the new tool project to the solution. |
| README.md | Updates top-level documentation for the two install paths + Tailwind version. |
| docs/PROJECT_STATUS.md | Updates Tailwind version references. |
| docs/COMPONENT_ROADMAP.md | Updates Tailwind version references. |
| .github/workflows/ci.yml | Adds a NuGet-only smoke step verifying safelist emission in a consumer build. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+41
to
+45
| Directory.CreateDirectory(Path.GetDirectoryName(txtOut)!); | ||
| File.WriteAllLines(txtOut, classes); | ||
|
|
||
| Directory.CreateDirectory(Path.GetDirectoryName(targetsOut)!); | ||
| File.WriteAllText(targetsOut, BuildTargetsFileContent(classes)); |
Comment on lines
+114
to
+123
| // Class-attribute extraction — handles literal strings, Razor expressions, nested | ||
| // Shell.Cn("foo", …) literals. Razor variable parts (`@Class`) are dropped because | ||
| // Tailwind would not see those at build time anyway. | ||
| private static readonly Regex ClassAttributeRegex = new( | ||
| "class\\s*=\\s*\"(?<value>[^\"]*)\"", | ||
| RegexOptions.Compiled); | ||
|
|
||
| private static readonly Regex StringLiteralRegex = new( | ||
| "\"(?<lit>[^\"]*)\"", | ||
| RegexOptions.Compiled); |
Comment on lines
+138
to
+154
| // A token qualifies as a Tailwind utility class if it contains at least one of | ||
| // `-`, `:`, `[`, `/` AND starts with a lowercase letter. Excludes C# identifiers | ||
| // (`Class`, `Variant`) caught by our literal extraction. | ||
| private static void HarvestTokens(string text, SortedSet<string> sink) | ||
| { | ||
| var tokens = text.Split(new[] { ' ', '\t', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); | ||
| foreach (var raw in tokens) | ||
| { | ||
| var token = raw.Trim(); | ||
| if (token.Length < 2) continue; | ||
| if (!char.IsLower(token[0])) continue; | ||
| if (!token.Contains('-') && !token.Contains(':') && !token.Contains('[') && !token.Contains('/')) continue; | ||
| token = token.TrimEnd(',', ';', ')', '"'); | ||
| if (token.Length < 2) continue; | ||
| sink.Add(token); | ||
| } | ||
| } |
Comment on lines
+6
to
+11
| public static class TailwindConstants | ||
| { | ||
| public const string Version = "4.3.2"; | ||
| public const string GitHubTag = "v" + Version; | ||
| public const string NpmRange = "^" + Version; | ||
| } |
Comment on lines
+88
to
+89
| var msg = "Safelist is out of date.\n"; | ||
| msg += "Regenerate with:\n dotnet run --project tools/ShellUI.SafelistGenerator -- src/ShellUI.Components/Components src/ShellUI.Components/wwwroot/shellui-classes.txt\n\n"; |
| ``` | ||
|
|
||
| That's it. Components render styled out of the box. Re-run `shellui add` whenever you want more. | ||
| The package ships a `shellui-classes.txt` safelist at `wwwroot/shellui-classes.txt` in your project. Wire Tailwind to scan it: |
Comment on lines
+26
to
+38
| After restore, the safelist appears at `wwwroot/shellui-classes.txt` in your project. In your `wwwroot/input.css`: | ||
|
|
||
| ```css | ||
| @import "tailwindcss"; | ||
| @source "./shellui-classes.txt"; | ||
|
|
||
| /* ... your theme variables, custom layers, etc. ... */ | ||
| ``` | ||
|
|
||
| Then in your `App.razor` or `_Host.cshtml`: | ||
| ```html | ||
| @using ShellUI.Components | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the documented gap for NuGet consumers:
dotnet add package ShellUI.Componentsnow produces styled components when the consumer's project has Tailwind set up. The package ships ashellui-classes.txtsafelist that Tailwind scans at build time via one@sourcedirective — tree-shaken, only classes actually used end up in compiled CSS. Also bumps Tailwind to 4.3.1.Changes
TailwindConstantsclass (single source of truth — downloader, npm install, config default, docs all derive from it)tools/ShellUI.SafelistGeneratorconsole tool — scans.razorfiles, extracts every Tailwind utility class (handles arbitrary values, modifiers, opacity, important), emits sorted-unique listsrc/ShellUI.Components/wwwroot/shellui-classes.txt— 307 classes from 147 razor files. Ships as a NuGet contentFile; restore copies it into the consumer'swwwroot/where Tailwind's@sourcedirective can scan itSafelistDriftTests— fails CI with a precise diff if anyone updates a component but forgets to regeneratedotnet add packagelands the safelist + a sanity-check grepVerification
dotnet packproduces nupkg with safelist atcontentFiles/any/net10.0/wwwroot/shellui-classes.txthover:,dark:,focus-visible:), arbitrary values (data-[state=open]:bg-accent,ease-[cubic-bezier(...)]), opacity (bg-background/80), important (!flex)