cssasp.net-corerazormsbuildblazor

How to embed the scoped css bundle file generated by Razor class library?


I'm looking to embed the scoped css bundle file that a razor class library project generates. This file is placed on the obj folder upon build. I'm having a challenge defining the correct msbuild target to emit the embedded resource at the right time during the build.

After much digging into the dotnet target files, this is what I currently have in the .csproj file.

<Target Name="EmbedScopedCss" BeforeTargets="BeforeResGen" AfterTargets="BundleScopedCssFiles">
    <ItemGroup>
        <EmbeddedResource Include="$(IntermediateOutputPath)scopedcss\bundle\$(PackageId).styles.css" Type="Non-Resx" WithCulture="false" />
    </ItemGroup>
</Target>

This is supposed to make the embedded resource item be emitted just before the BeforeResGen target, which is the last moment, to my knowledge, before resources are enumerated to be embedded into the assembly.

However, this still appears to be running too early in the build process, as the file itself does not exist at this time.

I have inferred AfterTargets="BundleScopedCssFiles" from the razor sdk scoped css target file but it doesn't appear to be enough.

Is there a way to do this?


Solution

  • If I correctly understand the MSBuild logs, the target that is responsible for CSS bundle generation for the current project is invoked after the project's assembly is already compiled.

    Personally I have addressed this problem by using a separate Razor class library project for storing all components with scoped stylesheets. Then I mark its generated .styles.css file as EmbeddedResource in the main project's .csproj file:

      <PropertyGroup>
        <!-- Disable CSS bundle generation for the main project as we cannot embed it -->
        <DisableScopedCssBundling>true</DisableScopedCssBundling>
    
        <!-- Define the base namespace for embedded CSS bundles -->
        <ScopedCssEmbeddedResourceNamespace>$(RootNamespace).wwwroot</ScopedCssEmbeddedResourceNamespace>
      </PropertyGroup>
    
      <Target Name="EmbedScopedCssBundles" AfterTargets="BundleScopedCssFiles" BeforeTargets="MainResourcesGeneration">
        <!-- Search for generated *.styles.css files for the current build configuration in neighbor projects and embed them -->
        <ItemGroup>
          <ScopedCssBundle Include="$(ProjectDir)../**/$(Configuration)/**/scopedcss/bundle/*.styles.css" />
          <ScopedCssEmbeddedResource Include="@(ScopedCssBundle)"
                                     ResourceName="$(ScopedCssEmbeddedResourceNamespace).%(Filename)%(Extension)" />
          <EmbeddedResource Include="@(ScopedCssEmbeddedResource)"
                            LogicalName="%(ScopedCssEmbeddedResource.ResourceName)" />
        </ItemGroup>
    
        <!-- Optional messages for MSBuild log -->
        <Message Condition="'@(ScopedCssEmbeddedResource)' == ''"
                 Text="No scoped CSS bundles found for $(Configuration) configuration" />
        <Message Condition="'@(ScopedCssEmbeddedResource)' != ''"
                 Text="Embedding scoped CSS bundles for $(Configuration) configuration:" />
        <Message Condition="'@(ScopedCssEmbeddedResource)' != ''"
                 Text="  %(ScopedCssEmbeddedResource.Identity) -> %(ScopedCssEmbeddedResource.ResourceName)" />
      </Target>
    

    If the main project is named MyProject and the neighbor Razor class library project is named MyProject.RazorLib, the code above results in the scoped CSS bundle will be embedded in the main project's assembly under the name MyProject.wwwroot.MyProject.RazorLib.styles.css.

    The downside of this approach is that every embedded CSS bundle must be referenced in markup individually.