I heard from the kind folks over on the C# discord that using Razor or Blazor for web facing applications is a bad idea. Apparently neither are really ready for the task; something to do with performance issues on both counts?
I had been learning razor because I want to build a web application – just learning, you know? – and it's a pretty sweet templating language that lets you inline C# straight into the web page. Given what I was told, I asked about what I should use instead and someone mentioned Svelte.
So I've started looking into Svelte, and I have to say it is rather pleasant so far.
On that note, I'd like to run a mono-repo with my front end and back end in the same repo. I've found a few tutorials that should what the boilerplate should look like.
Who Hosts Whom?
Maybe its "whom hosts who"? Anyway, we've got two build tools to contend with, dotnet
and npm
. I've found two tutorials, the first hosts npm
in dotnet
, that is the dotnet
build tool kicks off the npm
and rollup
build tools; and the second does the opposite.
I'm going to do the former because dotnet
is my preferred build orchestrator. For now. Maybe I'll change my mind?
[0] is the tutorial I've followed up to a point. It ends with integrating Svelte into Razor pages and I don't want to do that. I want to serve a whole front end that is only Svelte. So we'll follow this up to a point.
package.json
{
"name": "HnClone",
"version": "0.1.0",
"scripts": {
"build": "rollup -c rollup.config.js"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.2.3",
"rollup": "^4.6.1",
"rollup-plugin-svelte": "^7.1.6",
"svelte": "^4.2.8"
}
}
Oh, yeah. I'm writing a clone of Hacker News [1]. :D
Make sure to check for the latest version of the packages above. A simple npm view {package} version
[2] will get you what you need.
SvelteApp/
Create a folder called SvelteApp
and put the following in there.
SvelteApp/main.js
import App from "./App.svelte"
const app = new App({
target: document.body,
props: {
name: "Title"
}
})
export default app;
App.svelte
<svelte:options tag="svelte-app" />
<script>
export let name;
export let id;
</script>
<main>
<h1 id="{id}">Hello {name}!</h1>
More text.
</main>
<style>
h1 {
font-size: 5em;
}
</style>
SvelteApp/index.html
It took me a bit to sort out how to get anything served. Apparently we need either a static index.html
that we can work from (this smells like a Single Page App – SPA) OR we need to pre-render the HTML during build time. I don't think anyone has sorted out server side rendering (SSR) with ASP.NET and Svelte yet.
<!doctype html>
<html>
<head>
<meta charset='utf8'>
<meta name='viewport' content='width=device-width'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='favicon.ico'>
<link rel='stylesheet' href='lib/bootstrap/dist/css/bootstrap.css'>
</head>
<body>
<script src='js/bundle.js'></script>
<script src="/_framework/aspnetcore-browser-refresh.js"></script>
</body>
</html>
I borrowed this from [2].
rollup.config.mjs
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
export default {
// This `main.js` file we wrote
input: 'SvelteApp/main.js',
output: {
// The destination for our bundled JavaScript
file: 'wwwroot/js/bundle.js',
// Our bundle will be an Immediately-Invoked Function Expression
format: 'iife',
// The IIFE return value will be assigned into a variable called `app`
name: 'app',
},
plugins: [
svelte({
// Tell the svelte plugin where our svelte files are located
include: 'SvelteApp/**/*.svelte',
emitCss: false,
compilerOptions: {
customElement: true,
// we'll extract any component CSS out into
// a separate file - better for performance
css: "external"
}
}),
// Tell any third-party plugins that we're building for the browser
resolve({ browser: true }),
]
};
*.csproj
We add the following lines to the project's .csproj
file.
First we register npm
as part of our build process.
<Target Name="Rollup" BeforeTargets="Build">
<Exec Command="npm run build"/>
</Target>
Next, I wanted to auto-rebuild while using dotnet watch run
, especially after I've made changes to the Svelte side of things. I had to reference [3],[4] to get this done. dotnet
has an outstanding issue in [4] that's been open since 2021 where all of the files and folders in a project are watched for changes. Normally this isn't a huge deal, but looking at the output of dotnet watch --list
kinda hurt.
The first incantation clears the watch list of everything.
The Include
and Exclude
fields repopulate and filter the watch list.
<ItemGroup>
<Content Update="@(Content)" Watch="false"/>
<Watch
Include="**\*.cs;**\*.js;**\*.json;**\*.html;**\*.svelte;"
Exclude="node_modules\**\*;wwwroot\**\*;bin\**\*;obj\**\*;"
/>
We need to move our static index.html
file into wwwroot
. I decided to do this so that I could exclude the wwwroot
directory from the watch list. I don't know how I feel about this yet. I might undo it and put the Svelte app in the wwwroot
dir. Or something. I'm not sure yet.
<Content Update="SvelteApp\index.html">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<Watch>false</Watch>
</Content>
</ItemGroup>
<Target Name="MoveStaticFiles" AfterTargets="AfterBuild">
<Copy SourceFiles="SvelteApp/index.html" DestinationFolder="wwwroot"/>
</Target>
Serving Svelte Pages
Now we should be able to run dotnet watch run
and everything should work.
Source Code
Here's the code for this. It isn't "clean", but it should get you going in the right direction.
https://gitlab.com/comalice/svelte-dotnet-8
Links
- [0] https://khalidabuhakmeh.com/add-svelte-to-aspnet-core-projects
- [0a] https://dev.to/cainux/net-core-and-svelte-f8o << This walkthrough hosts
dotnet
innpm
. - [1] https://news.ycombinator.com
- [2] https://stackoverflow.com/a/11949502/7823006
- [3] https://khalidabuhakmeh.com/watching-more-files-with-dotnet-watch-for-static-sites
- [4] https://github.com/dotnet/aspnetcore/issues/31141#issuecomment-805229841