I'm currently trying to integrate a single page app into a C# .Net Core 3.1 project but I'm struggling to convince MSBuild to compile the spa and bundle it into the runtime executable.
The SPA uses Elm + create-elm-app and thus doesn't use npm. I've tweaked my .csproj file to delegate to elm-app build
but this isn't invoked. Deliberately breaking the Elm code to force build failures again has no effect. Please note, I've replaced node_modules with elm-stuff which is where dependencies etc are downloaded to. I'm presuming this is something to do with my ItemGroups but the source files are recognised in my (Rider) project browser. Also note,
Interesting parts of my .csproj:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<SpaRoot>ClientApp\</SpaRoot>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)elm-stuff\**</DefaultItemExcludes>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="EventStore.ClientAPI.NetCore" Version="4.1.0.23" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.1.4" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.4" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.4" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.4" />
</ItemGroup>
<ItemGroup>
<!-- Don't publish the SPA source files, but do show them in the project files list -->
<Content Remove="$(SpaRoot)**" />
<None Remove="$(SpaRoot)**" />
<None Include="$(SpaRoot)**" Exclude="$(SpaRoot)elm-stuff\**" />
</ItemGroup>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec WorkingDirectory="$(SpaRoot)" Command="elm-app build" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="$(SpaRoot)build\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>
Interesting parts of my Startup.cs
public void ConfigureServices(IServiceCollection services)
{
....
if (_environment.IsDevelopment())
{
services.AddControllersWithViews()
.AddRazorRuntimeCompilation();
}
else
{
services.AddControllersWithViews();
}
services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/build"; });
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
....
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (_environment.IsDevelopment())
{
spa.UseProxyToSpaDevelopmentServer("http://localhost:3000/");
}
});
}
And the result of a dotnet build showing successful compilation of C# source, but no mention of delegation or failure to delegate to elm-app:
will@cafell Application % dotnet build
Microsoft (R) Build Engine version 16.5.0+d4cbfca49 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 49.73 ms for /Users/will/src/startups/ideasforphotography/ideasforphotography/IdeasForPhotography.Framework/IdeasForPhotography.Framework.csproj.
Restore completed in 49.74 ms for /Users/will/src/startups/ideasforphotography/ideasforphotography/Application/Application.csproj.
Restore completed in 49.74 ms for /Users/will/src/startups/ideasforphotography/ideasforphotography/IdeasForPhotography.Domain.ClubIdeas/IdeasForPhotography.Domain.ClubIdeas.csproj.
IdeasForPhotography.Framework -> /Users/will/src/startups/ideasforphotography/ideasforphotography/IdeasForPhotography.Framework/bin/Debug/netcoreapp3.1/IdeasForPhotography.Framework.dll
IdeasForPhotography.Domain.ClubIdeas -> /Users/will/src/startups/ideasforphotography/ideasforphotography/IdeasForPhotography.Domain.ClubIdeas/bin/Debug/netcoreapp3.1/IdeasForPhotography.Domain.ClubIdeas.dll
Clubs/ClubApplicationService.cs(19,21): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). [/Users/will/src/startups/ideasforphotography/ideasforphotography/Application/Application.csproj]
Infrastructure/EventDeserializer.cs(21,30): warning CS0168: The variable 'ex' is declared but never used [/Users/will/src/startups/ideasforphotography/ideasforphotography/Application/Application.csproj]
Application -> /Users/will/src/startups/ideasforphotography/ideasforphotography/Application/bin/Debug/netcoreapp3.1/Application.dll
Application -> /Users/will/src/startups/ideasforphotography/ideasforphotography/Application/bin/Debug/netcoreapp3.1/Application.Views.dll
Build succeeded.
Clubs/ClubApplicationService.cs(19,21): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). [/Users/will/src/startups/ideasforphotography/ideasforphotography/Application/Application.csproj]
Infrastructure/EventDeserializer.cs(21,30): warning CS0168: The variable 'ex' is declared but never used [/Users/will/src/startups/ideasforphotography/ideasforphotography/Application/Application.csproj]
2 Warning(s)
0 Error(s)
Time Elapsed 00:00:03.34
So, this configuration does work when I publish rather than just build the application, specifically using this command
dotnet publish -c Release