Blazorators
C# Source Generator for JavaScript Interop in Blazor
Blazorators
Blazorators is a C# source generator that creates fully functioning Blazor JavaScript interop code, targeting either the IJSInProcessRuntime
or IJSRuntime
types. All of this code relies on the output of one of my side projects named blazorators
. While writing my O’Reilly “Learning Blazor: Build Single-Page Apps with WebAssembly and C#” book, I discovered an impactful way to use both TypeScript type declarations from common JavaScript APIs and C# source generators to generate the JavaScript interop code. This is covered in chapter seven, so I don’t want to spoil it too much, but rather entice you to want to read the book. I’ve been using this approach in my Blazor apps for a while now and I’m excited to share it with you. Let’s get started!
Check out my new Twitter account (@blazorbits) for Blazor tips and tricks! Below you’ll see the Progress Telerik REPL (Read Evaluate Print Loop) for Blazor, select the Preview tab and watch the Blazor WebAssembly app perform a .NET restore and builds the app. Once the app loads, select a Natural voice from the voice selection control if it is available to you. If it’s not available to you, that’s not a big deal either. Select the Speak button and the app will speak the text you entered in the Text-to-speech control. You can also select the Stop button to stop the speech.
Explore the app source code
when you create your own REPL session you’re free to consume public NuGet packages, this app uses the Blazor.SpeechSynthesis.WebAssembly
NuGet package 📦.
Explore the Code tab thoroughly and look through the app’s source code. You’ll see that this app does the following:
- _Main.razor: The app’s main page, that showing a loading indicator and immediately navigates to the /speak route.
- TextToSpeech.razor: The page that renders the Text-to-speech text input element and the browser-native speech synthesis control buttons.
- TextToSpeech.razor.cs: The page’s code-behind that contains the
TextToSpeech
component’s logic. - Startup.cs: The app’s startup class that configures services, calling a source-generated dependency injection specific extension method named `AddSpeechServices.
Notice the SpeechSynthesisVoice[]
, SpeechSynthesisUtterance
, and ISpeechSynthesisService
types. These are all from the Blazor.SpeechSynthesis.WebAssembly
NuGet package 📦. The SpeechSynthesisVoice[]
type is an array of SpeechSynthesisVoice
objects that represent the available voices on the user’s device. The SpeechSynthesisUtterance
type is a class that represents the text to be spoken and the voice to be used. The ISpeechSynthesisService
type is an interface that defines the GetVoicesAsync
and an in-process Speak
method that is synchronous.
The Speak
function takes a SpeechSynthesisUtterance
object and a callback function that is invoked when the speech is complete, delivering the total elapsed time in milliseconds.
There is also a way to listen for changes in the browser-native window.speechSynthesis
voices that are available to the JavaScript context. Provide a callback to the ISpeechSynthesisService.OnVoicesChanged
event and it will be invoked when the window.speechSynthesis
voiceschanged
event is fired. This is useful if you want to update the UI with the available voices. All of this functionality is source-generated by the Blazor.SourceGenerator
project.
What’s next?
This project was originally written as part of a Microsoft hackathon. It’s been refined, but is still a brute-force effort and could stand to be optimized. As such I’m working to reimplement the parsing approach. It currently makes an HTTP request to a raw Github link with the lib.dom.d.ts file with all of the DOM type declarations in it. The file is itself, generated, and is over 18 thousand lines of code. It uses Regex
expressions to parse on-demand single hierarchical type dependency graphs as readonly record struct
values. These values were modeled as
Patent pending, not… ☹️
I was working with Stephen Toub (the patent lead for the .NET team), and we filed it for a patent. I was disappointed to learn that it wasn’t filed as the lawyers reviewed it and determined that it wasn’t patentable. I’m not sure why, but I’m glad to share it with you. I hope you find it useful. If you have any questions, please feel free to reach out to me:
The community is excited about this project, and I’m excited to see what you build with it. Here’s what some of the community members have said on Twitter:
@davidpine7 Hey man, I’m looking to use Blazorators to generate some JS APIs for me. Is there a way to point it to a .ts or .d.ts file and have it spit out the Blazor APIs that implement it?
— Robert McLaws (@robertmclaws) April 2, 2022
Found the parser in the lib now. That is so cool! 😀 I will check it out. Seems like it would be easy to extend the existing code if some 'WebIDLParser' just has the same methods as your `LibDomParser`.
— Kristoffer Strube (@KStrubeG) March 13, 2022
I love this project name. Blazorators, mount up!
— Scott Addie (@Scott_Addie) March 25, 2022
This is crazy, check out what @davidpine7 is up to https://t.co/s3lpwXSzFE
— Ed Charbeneau (@EdCharbeneau) March 16, 2022
The plan
I’m rewriting the TypeScript abstract syntax tree parser to C#, essentially porting it one-to-one. When this is done, the parsing will be much more accurate and flexible. Likewise, our friends over on Twitter, and some of the community members on YouTube have been kind to me as well. Thank you for your kind words, they mean the world to me.
This is a pretty amazing project. Hope this makes it into a serious project at Microsoft! Thank you!
fieryscorpion
I think this package has a lot of potential. It makes the bridge between JS and C# in Blazor much less painful. I love it! Well done, David.
Jiří Novotný
Additional resources
Share this post
Sponsor
Twitter
Facebook
Reddit
LinkedIn
StumbleUpon
Email