Skip to content

Build Blazor page/component and publish it to npm/nuget #16031

@DrSensor

Description

@DrSensor

TL; DR: this is more like feature proposal for future Blazor Cli

Taking inspiration from Vue that can build SFC (Single File Component) *.vue into redistributable package, it would be nice if Blazor can do the same thing. Because Blazor can be compiled into *.dll (AOT compiled mode) and .wasm (interpreted mode), it also make sense to have ability to build and publish single Blazor Component (.cshtml) into NuGet and/or NPM for shareabilitiy.

Given this Blazor Component (if in the future Blazor support F#)

with usage

<Propeller diameter="120cm" power="24W" propconstant="3.2" scale="0.5px/cm" />

then the implementation of Propeller.fshtml will be

<!-- https://codepen.io/drsensor/pen/ELEzWb -->
<svg width="@Diameter" height="@Diameter">
  <g transform="translate(0 @(Diameter/4))">
    <rect width="@Diameter" height="@(Diameter/2)" rx="@(Diameter/2)" ry="@(Diameter/4)" >
          <animateTransform attributeName="transform"
                          type="rotate"
                          from="0 @(Diameter/2) @(Diameter/4)"
                          to="360 @(Diameter/2) @(Diameter/4)"
                          dur="@Period"
                          repeatCount="indefinite"/>
    </rect>
    <rect width="@Diameter" height="@(Diameter/2)" rx="@(Diameter/2)" ry="@(Diameter/4)" >
      <animateTransform attributeName="transform"
                        type="rotate"
                        from="90 @(Diameter/2) @(Diameter/4)"
                        to="450 @(Diameter/2) @(Diameter/4)"
                        dur="@Period"
                        repeatCount="indefinite"/>
    </rect>
  </g>
  <text x="25%" y="10%">@Rpm RPM</text>
  <text x="75%" y="90%">@Thrust N</text>
</svg>

@functions {
  /// https://quadcopterproject.wordpress.com/static-thrust-calculation/
  [<Measure>] type rad (* revolution or radians *)
  [<Measure>] type s (* second *)
  [<Measure>] type m (* meter *)
  [<Measure>] type px (* pixels *)
  [<Measure>] type kg (* kilogram *)
  [<Measure>] type N = (kg*m)/(s^2) (* Newtons *)
  [<Measure>] type W = (N*m)*(rad/s) (* Watts *) (* P = τ * ω *)
  let π = 3.14159265359
  let HertzOf (rpm : <rad/s>) : float<rad/s> = rpm/60

  [<Measure>] let cm = 1/100<m>

  let public scale : float<px/m> = 1.0<px/m>
  val public power : float<W>
  val public diameter : float<m>
  val public propconstant : float<N*m>

  let public propRpm powerFactor : float<rad/s> = (power/propconstant)^(1/powerFactor)

  member Rpm with get () = propRpm(3.2)
  member Thrust : float<N>
    with get () = ((π/2) * (diameter^2) * 1.225<kg/m^3> * (power^2) )^(1/3)

  member Period : float<script> with get () = 1/(HertzOf(Rpm))
  member Diameter : int<px> with get () = diameter*scale
}

Build and Publish to NPM

Taking consideration that WebComponent became widely adopted, there is 2 build target that essential for sharing Blazor Component into another JS/TS based only project.

Build as WebAssembly

not sure about this

CLI

dotnet blazor build --target wasm Propeller.fshtml
cd dist
npm version minor
npm publish
For building multiple Blazor Component into single wasm (not usre if this good)

given the Propeller.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <EnableDefaultItems>false</EnableDefaultItems>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.FSharp" Version="*" />
    <Compile Include="PropellerAPC.fshtml" />
    <Compile Include="PropellerWD.cshtml" />
    <Compile Include="PropellerCF.cshtml" />
  </ItemGroup>
</Project>

then build the .csproj file

dotnet blazor build --target wasm Propeller.csproj

Still don't know if it's best to output single Propeller.wasm or multiple Propeller*.wasm ?

Usage

  • using webpack and wasm-loader

waiting for docs how to use webpack 4 webassembly/experimental

import propeller from 'propeller'

propeller().then(instance => {
  // https://quadcopterproject.wordpress.com/static-thrust-calculation/
  const calcrpm = instance.exports.propRpm
  const rpm = calcrpm(3.2)
  console.log(`${rpm} RPM`) // 24 Watt
})
  • using fetch (usefull for dynamic loading at runtime via CDN)
<button onclick="instantiatePropeller()">load component</button>
<p id="rpm" />
<script>
function instantiatePropeller() {
  WebAssembly.instantiateStreaming(
    fetch('https://cdn.jsdelivr.net/npm/propeller/dist/propeller.wasm'), importObject)
    .then(obj => {
      document.getElementById('rpm').textContent = obj.instance.exports.calcrpm(3.2);
    }
  );
}
</script>

Build as WebComponent

If Blazor heading toward to support WebComponent

The produced WebComponent are just binding to the generated WebAssembly.

CLI

dotnet blazor build --target wc Propeller.fshtml
cd dist
npm version minor
npm publish

Usage

  • via CDN
<head>
  <script src="https://cdn.jsdelivr.net/npm/propeller" />
</head>
<body>
  <propeller diameter="120cm" power="24W" propconstant="3.2" scale="0.5px/cm" />
</body>
For building multiple Blazor Component into single `propeller.min.js`

the Propeller.csproj same as wasm

dotnet blazor build --target wc Propeller.csproj

using multiple component

<head>
  <script src="https://cdn.jsdelivr.net/npm/propeller" />
</head>
<body>
  <propeller-apc diameter="120cm" power="24W" scale="0.5px/cm" />
  <propeller-w diameter="60cm" power="24W" scale="0.5px/cm" />
  <propeller-cf diameter="80cm" power="24W" scale="0.5px/cm" />
</body>

Build and Publish to NuGet

While dotnet cli can build standalone blazor component, it would be nice to have dedicated cli for building standalone blazor component without tinkering with .csproj file.

CLI

dotnet blazor build Propeller.fshtml
cd nupkgs
dotnet nuget push Propeller.nupkg

Usage

adding to .csproj

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Blazor.Browser" Version="*" />
    <PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="*" />
    <PackageReference Include="Propeller" Version="*" />
  </ItemGroup>
</Project>

In PropellerAPC.cshtml

@addTagHelper *, Propeller
@using Propeller

<Propeller power="@power" scale="@scale" diameter="@diameter" propconstant="4.6" />

@functions {
  public double scale { get; set; } = 1.0;
  public double power { get; set; }
  public double diameter { get; set; }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-blazorIncludes: Blazor, Razor Components

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions