Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ASP.NET] Add support for JS modules and initializers #19270

Merged
merged 9 commits into from
Aug 11, 2021

Conversation

javiercn
Copy link
Member

@javiercn javiercn commented Jul 26, 2021

This PR adds support for JS Modules and initializers:
dotnet/aspnetcore#26145
dotnet/aspnetcore#32353

JS modules are discovered based on the following patterns:

// For components
**.razor.js"
// For Mvc pages and views
**.cshtml.js"

We discover these files and copy them to the output folder in the right location (we strip the pages, shared, views, components, prefix)

Blazor applications can load these modules in the same way they load other modules, the only addition is that we define this convention instead of you having to do it manually.

For JS initializers:

  • Each project/package can define a file with the name $(PackageId).lib.module.js which will be detected as a library initializer.
  • Blazor webassembly will embed the list of initializers within blazor.boot.json and will import them automatically during the boot process. In the Blazor case, the initializers can export a couple of well-known functions to customize the blazor loading process, but that is outside of the scope of this PR.
  • For other types of applications a json file is automatically generated as part of the build that includes the list of initializers for the application.
    • Blazor server will read this file and emit its contents to the generated HTML when it writes the first component on to the page or if no component was emitted, it will automatically load it on start.
    • Blazor desktop will just load the file as part of the boot process.

MVC/Razor pages applications won't automatically load any of the library initializers, however, it is trivial to include a small script to fetch the manifest and trigger the load of all the library initializers.

Finally, this PR includes a small set of changes to the Blazor publish process to enable customizing the blazor build process. We do this by collecting the list of Blazor files and writing a file to disk containing their full paths. That new file is included in the blazor.boot.json manifest in a new extensions section that contains a dictionary of custom extensions by name.

This is enough to prove that users can customize the Blazor build process inside their app or from an external nuget package to suit your requirements. Upon boot (outside of the scope of this PR), blazor will load the blazor.boot.json manifest, load the library initializers and invoke them giving them their options, from which they can customize the remaining loading process.

This PR has been updated based on our discussions

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label.

@javiercn javiercn added the Area-AspNetCore RazorSDK, BlazorWebAssemblySDK, dotnet-watch label Jul 26, 2021
@javiercn javiercn requested a review from pranavkm July 26, 2021 13:19
@javiercn javiercn marked this pull request as ready for review July 27, 2021 18:40
@javiercn javiercn changed the base branch from main to javiercn/static-web-assets-bring-back-compat August 4, 2021 17:39
@javiercn
Copy link
Member Author

javiercn commented Aug 4, 2021

This PR is broken down into three commits:

  • The first commit has the code and the tests.
  • The second commit updates existing baselines as a result of these changes.
  • The third commit includes the new baselines.

@javiercn javiercn force-pushed the javiercn/static-web-assets-bring-back-compat branch from d3159fe to 2c8f963 Compare August 6, 2021 10:40
<GetFileHash Files="@(_BlazorJsModuleCandidatesForBuildWithTargetPath)" Algorithm="SHA256" HashEncoding="base64">
<Output TaskParameter="Items" ItemName="_BlazorOutputWithHash" />
</GetFileHash>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

@@ -368,8 +409,8 @@ Copyright (c) .NET Foundation. All rights reserved.
</ItemGroup>
</Target>

<Target Name="_ProcessPublishFilesForBlazor" DependsOnTargets="LoadStaticWebAssetsBuildManifest" AfterTargets="ILLink">

<Target Name="ProcessPublishFilesForBlazor" DependsOnTargets="LoadStaticWebAssetsBuildManifest" AfterTargets="ILLink">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meant to be a user extensibility point? Is there a reason they cannot configure itemgroups that are consumed by this globally (outside of a target)? We've had a terrible record of telling people to hook up to targets and taking it away when we refactor or change our implementation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to compute the list of Blazor assets, which happens in this target, and then we pick up any potential result from modifying it, and we add it to the list of assets.

I went this route becasue I believe its the simplest for customers, do you have any other way in mind that keeps the simplicity and gives us more flexibility?

The other thing I can think of is to get them to add themselves to a property group, and we'll make sure they are always called at the right time, how does that sound?

src/RazorSdk/Tasks/GenerateJSModuleManifest.cs Outdated Show resolved Hide resolved
@javiercn javiercn force-pushed the javiercn/static-web-assets-bring-back-compat branch from 2c8f963 to ddaddf4 Compare August 7, 2021 13:03
@javiercn javiercn force-pushed the javiercn/static-web-assets-bring-back-compat branch from 190fc7f to 1dd270d Compare August 7, 2021 17:27
@javiercn javiercn force-pushed the javiercn/js-initializers branch 2 times, most recently from ca1940c to cbcd250 Compare August 9, 2021 14:42
@javiercn javiercn changed the base branch from javiercn/static-web-assets-bring-back-compat to main August 9, 2021 14:42
@StevenRasmussen
Copy link

This is a great addition! I'm sure the docs are coming for this but in the mean time is there anything special that needs to happen to light this up? I'm trying to test this out but I'm not seeing any of my .js files copied anywhere. Where should they be going? I checked both the 'bin' and 'obj' folders. I see the 'scopedcss' folder with assets from my isolated css files. I have a component Overlay.razor in the root like this:
image

Here are the bin and obj folders:
image

Thanks!

@javiercn
Copy link
Member Author

@StevenRasmussen they only get copied at publish time.

You can edit them in place and they will be served from there.

@StevenRasmussen
Copy link

Thanks for such a quick reply! So just to clarify: while debugging I shouldn't see them placed in the wwwroot folder or anything? Blazor just knows that I'm debugging and so it uses the original file or something? But I would still load them like:

var module = await JS.InvokeAsync<IJSObjectReference>("import", "./Overlay.razor.js");

@StevenRasmussen
Copy link

Hmmm.... I must be doing something wrong. Here's my C#:

protected override async Task OnAfterRenderAsync(bool firstRender)
{
	await base.OnAfterRenderAsync(firstRender);
	_module = await JS.InvokeAsync<IJSObjectReference>("import", "./Overlay.razor.js");
}

Error message in the browser after the call to JS.InvokeAsync to try and load the module:
image

Also, I'm happy to open a new ticket to further the discussion as I know this is getting off-topic for the PR itself. Was just hoping that there was something simple I was missing (which could still be the case as this is my first foray into loading modules in js).

@StevenRasmussen
Copy link

Ok. Sorry about the posts but I think this is resolved. I guess you need to specify a different path when using a Razor Class Library. Using the following seemed to work:

_module = await JS.InvokeAsync<IJSObjectReference>("import", "./_content/RazorClassLibrary/Overlay.razor.js");

If I have any further questions I'll open up a new ticket. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-AspNetCore RazorSDK, BlazorWebAssemblySDK, dotnet-watch
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants