Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Components/Web.JS/dist/Release/blazor.webassembly.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Components/Web.JS/src/Platform/BootConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface ResourceGroups {
readonly assembly: ResourceList;
readonly pdb?: ResourceList;
readonly runtime: ResourceList;
readonly satelliteResources?: { [cultureName: string] : ResourceList };
}

export type ResourceList = { [name: string]: string };

31 changes: 31 additions & 0 deletions src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,37 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade
// of the extensions in the URLs. This allows loading assemblies with arbitrary filenames.
assembliesBeingLoaded.forEach(r => addResourceAsAssembly(r, changeExtension(r.name, '.dll')));
pdbsBeingLoaded.forEach(r => addResourceAsAssembly(r, r.name));

// Wire-up callbacks for satellite assemblies. Blazor will call these as part of the application
// startup sequence to load satellite assemblies for the application's culture.
window['Blazor']._internal.getSatelliteAssemblies = (dotnetArray: Pointer) : Pointer => {
const culturesToLoad = BINDING.mono_array_to_js_array<string>(dotnetArray);
const satelliteResources = resourceLoader.bootConfig.resources.satelliteResources;

if (satelliteResources) {
const resourcePromises = Promise.all(culturesToLoad
.filter(culture => satelliteResources.hasOwnProperty(culture))
.map(culture => resourceLoader.loadResources(satelliteResources[culture], fileName => `_framework/_bin/${fileName}`))
.flat()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried your suggestion and it looks like we need to target es2019 to use it. Are there downsides to doing that?

Copy link
Member

Choose a reason for hiding this comment

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

Browser support for .flat looks reasonable, however it's not like we're saving ourselves dozens of lines of code here.

Your original implementation looks totally fine to me, too!

.map(async resource => (await resource.response).arrayBuffer()));

return BINDING.js_to_mono_obj(
resourcePromises.then(resourcesToLoad => {
if (resourcesToLoad.length) {
window['Blazor']._internal.readSatelliteAssemblies = () => {
const array = BINDING.mono_obj_array_new(resourcesToLoad.length);
for (var i = 0; i < resourcesToLoad.length; i++) {
BINDING.mono_obj_array_set(array, i, BINDING.js_typed_array_to_array(new Uint8Array(resourcesToLoad[i])));
}
return array;
};
}

return resourcesToLoad.length;
}));
}
return BINDING.js_to_mono_obj(Promise.resolve(0));
}
});

module.postRun.push(() => {
Expand Down
7 changes: 5 additions & 2 deletions src/Components/Web.JS/src/Platform/Mono/MonoTypes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Pointer, System_String } from '../Platform';
import { Pointer, System_String, System_Array, System_Object } from '../Platform';

// Mono uses this global to hang various debugging-related items on

Expand All @@ -10,9 +10,12 @@ declare interface MONO {

// Mono uses this global to hold low-level interop APIs
declare interface BINDING {
mono_obj_array_new(length: number): System_Array<System_Object>;
mono_obj_array_set(array: System_Array<System_Object>, index: Number, value: Pointer): void;
js_string_to_mono_string(jsString: string): System_String;
js_typed_array_to_array(array: Uint8Array): Pointer;
js_typed_array_to_array<T>(array: Array<T>): Pointer;
js_to_mono_obj(jsObject: any) : Pointer;
mono_array_to_js_array<T>(object: Pointer) : Array<T>;
conv_string(dotnetString: System_String | null): string | null;
bind_static_method(fqn: string, signature?: string): Function;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using Microsoft.Build.Framework;
Expand Down Expand Up @@ -61,28 +62,56 @@ internal void WriteBootJson(Stream output, string entryAssemblyName)
cacheBootResources = CacheBootResources,
debugBuild = DebugBuild,
linkerEnabled = LinkerEnabled,
resources = new Dictionary<ResourceType, ResourceHashesByNameDictionary>(),
resources = new ResourcesData(),
config = new List<string>(),
};

// Build a two-level dictionary of the form:
// - BootResourceType (e.g., "assembly")
// - assembly:
// - UriPath (e.g., "System.Text.Json.dll")
// - ContentHash (e.g., "4548fa2e9cf52986")
// - runtime:
// - UriPath (e.g., "dotnet.js")
// - ContentHash (e.g., "3448f339acf512448")
if (Resources != null)
{
var resourceData = result.resources;
foreach (var resource in Resources)
{
var resourceTypeMetadata = resource.GetMetadata("BootManifestResourceType");
if (!Enum.TryParse<ResourceType>(resourceTypeMetadata, out var resourceType))
ResourceHashesByNameDictionary resourceList;
switch (resourceTypeMetadata)
{
throw new NotSupportedException($"Unsupported BootManifestResourceType metadata value: {resourceTypeMetadata}");
}

if (!result.resources.TryGetValue(resourceType, out var resourceList))
{
resourceList = new ResourceHashesByNameDictionary();
result.resources.Add(resourceType, resourceList);
case "runtime":
resourceList = resourceData.runtime;
break;
case "assembly":
resourceList = resourceData.assembly;
break;
case "pdb":
resourceData.pdb = new ResourceHashesByNameDictionary();
resourceList = resourceData.pdb;
break;
case "satellite":
if (resourceData.satelliteResources is null)
{
resourceData.satelliteResources = new Dictionary<string, ResourceHashesByNameDictionary>(StringComparer.OrdinalIgnoreCase);
}
var resourceCulture = resource.GetMetadata("Culture");
if (resourceCulture is null)
{
Log.LogWarning("Satellite resource {0} does not specify required metadata 'Culture'.", resource);
continue;
}

if (!resourceData.satelliteResources.TryGetValue(resourceCulture, out resourceList))
{
resourceList = new ResourceHashesByNameDictionary();
resourceData.satelliteResources.Add(resourceCulture, resourceList);
}
break;
default:
throw new NotSupportedException($"Unsupported BootManifestResourceType metadata value: {resourceTypeMetadata}");
}

var resourceName = GetResourceName(resource);
Expand Down Expand Up @@ -142,7 +171,7 @@ public class BootJsonData
/// and values are SHA-256 hashes formatted in prefixed base-64 style (e.g., 'sha256-abcdefg...')
/// as used for subresource integrity checking.
/// </summary>
public Dictionary<ResourceType, ResourceHashesByNameDictionary> resources { get; set; }
public ResourcesData resources { get; set; } = new ResourcesData();

/// <summary>
/// Gets a value that determines whether to enable caching of the <see cref="resources"/>
Expand All @@ -166,11 +195,29 @@ public class BootJsonData
public List<string> config { get; set; }
}

public enum ResourceType
public class ResourcesData
{
assembly,
pdb,
runtime,
/// <summary>
/// .NET Wasm runtime resources (dotnet.wasm, dotnet.js) etc.
/// </summary>
public ResourceHashesByNameDictionary runtime { get; set; } = new ResourceHashesByNameDictionary();

/// <summary>
/// "assembly" (.dll) resources
/// </summary>
public ResourceHashesByNameDictionary assembly { get; set; } = new ResourceHashesByNameDictionary();

/// <summary>
/// "debug" (.pdb) resources
/// </summary>
[DataMember(EmitDefaultValue = false)]
public ResourceHashesByNameDictionary pdb { get; set; }

/// <summary>
/// localization (.satellite resx) resources
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Dictionary<string, ResourceHashesByNameDictionary> satelliteResources { get; set; }
}
#pragma warning restore IDE1006 // Naming Styles
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
<AdditionalMonoLinkerOptions>--disable-opt unreachablebodies --verbose --strip-security true --exclude-feature com -v false -c link -u link -b true</AdditionalMonoLinkerOptions>

<_BlazorJsPath Condition="'$(_BlazorJsPath)' == ''">$(MSBuildThisFileDirectory)..\tools\blazor\blazor.webassembly.js</_BlazorJsPath>
<_BaseBlazorDistPath>dist\</_BaseBlazorDistPath>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had to account for these in path manipulation. Removed since this isn't used any more.

<_BaseBlazorRuntimeOutputPath>$(_BaseBlazorDistPath)_framework\</_BaseBlazorRuntimeOutputPath>
<_BaseBlazorRuntimeOutputPath>_framework\</_BaseBlazorRuntimeOutputPath>
<_BlazorRuntimeBinOutputPath>$(_BaseBlazorRuntimeOutputPath)_bin\</_BlazorRuntimeBinOutputPath>
<_BlazorRuntimeWasmOutputPath>$(_BaseBlazorRuntimeOutputPath)wasm\</_BlazorRuntimeWasmOutputPath>
<_BlazorBuiltInBclLinkerDescriptor>$(MSBuildThisFileDirectory)BuiltInBclLinkerDescriptor.xml</_BlazorBuiltInBclLinkerDescriptor>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@
-->
<ItemGroup>
<!-- Assemblies from packages -->
<_BlazorManagedRuntimeAssemby Include="@(RuntimeCopyLocalItems)" />
<_BlazorManagedRuntimeAssembly Include="@(RuntimeCopyLocalItems)" />

<!-- Assemblies from other references -->
<_BlazorUserRuntimeAssembly Include="@(ReferencePath->WithMetadataValue('CopyLocal', 'true'))" />
<_BlazorUserRuntimeAssembly Include="@(ReferenceDependencyPaths->WithMetadataValue('CopyLocal', 'true'))" />

<_BlazorManagedRuntimeAssemby Include="@(_BlazorUserRuntimeAssembly)" />
<_BlazorManagedRuntimeAssemby Include="@(IntermediateAssembly)" />
<_BlazorManagedRuntimeAssembly Include="@(_BlazorUserRuntimeAssembly)" />
<_BlazorManagedRuntimeAssembly Include="@(IntermediateAssembly)" />
</ItemGroup>

<MakeDir Directories="$(_BlazorIntermediateOutputPath)" />
Expand All @@ -70,73 +70,68 @@
satellite assemblies, this should include all assemblies needed to run the application.
-->
<ItemGroup>
<_BlazorJSFile Include="$(_BlazorJSPath)" />
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Slight reshuffling to make it easier to view the binary logs

<_BlazorJSFile Include="$(_BlazorJSMapPath)" Condition="Exists('$(_BlazorJSMapPath)')" />
<_DotNetWasmRuntimeFile Include="$(ComponentsWebAssemblyRuntimePath)*" />

<!--
ReferenceCopyLocalPaths includes all files that are part of the build out with CopyLocalLockFileAssemblies on.
Remove assemblies that are inputs to calculating the assembly closure. Instead use the resolved outputs, since it is the minimal set.

ReferenceCopyLocalPaths also includes satellite assemblies from referenced projects but are inexpicably missing
any metadata that might allow them to be differentiated. We'll explicitly add those
to _BlazorOutputWithTargetPath so that satellite assemblies from packages, the current project and referenced project
are all treated the same.
-->
<_BlazorCopyLocalPaths Include="@(ReferenceCopyLocalPaths)" Condition="'%(Extension)' == '.dll'" />
<_BlazorCopyLocalPaths Remove="@(_BlazorManagedRuntimeAssemby)" />
<_BlazorCopyLocalPaths Include="@(ReferenceCopyLocalPaths)"
Exclude="@(_BlazorManagedRuntimeAssembly);@(ReferenceSatellitePaths)"
Condition="'%(Extension)' == '.dll'" />

<_BlazorCopyLocalPaths Include="@(IntermediateSatelliteAssembliesWithTargetPath)">
<DestinationSubDirectory>%(IntermediateSatelliteAssembliesWithTargetPath.Culture)\</DestinationSubDirectory>
</_BlazorCopyLocalPaths>

<_BlazorOutputWithTargetPath Include="@(_BlazorCopyLocalPaths)">
<!-- This group is for satellite assemblies. We set the resource name to include a path, e.g. "fr\\SomeAssembly.resources.dll" -->
<BootManifestResourceType Condition="'%(Extension)' == '.dll'">assembly</BootManifestResourceType>
<BootManifestResourceType Condition="'%(Extension)' == '.pdb'">pdb</BootManifestResourceType>
<BootManifestResourceType Condition="'%(_BlazorCopyLocalPaths.Culture)' == '' AND '%(_BlazorCopyLocalPaths.Extension)' == '.dll'">assembly</BootManifestResourceType>
<BootManifestResourceType Condition="'%(_BlazorCopyLocalPaths.Culture)' != '' AND '%(_BlazorCopyLocalPaths.Extension)' == '.dll'">satellite</BootManifestResourceType>
<BootManifestResourceName>%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)</BootManifestResourceName>
<TargetOutputPath>$(_BlazorRuntimeBinOutputPath)%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)</TargetOutputPath>
</_BlazorOutputWithTargetPath>

<_BlazorOutputWithTargetPath Include="@(_BlazorResolvedAssembly)">
<BootManifestResourceType Condition="'%(Extension)' == '.dll'">assembly</BootManifestResourceType>
<BootManifestResourceType Condition="'%(Extension)' == '.pdb'">pdb</BootManifestResourceType>
<BootManifestResourceName>%(FileName)%(Extension)</BootManifestResourceName>
<TargetOutputPath>$(_BlazorRuntimeBinOutputPath)%(FileName)%(Extension)</TargetOutputPath>
<_BlazorOutputWithTargetPath Include="@(ReferenceSatellitePaths)">
<Culture>$([System.String]::Copy('%(ReferenceSatellitePaths.DestinationSubDirectory)').Trim('\'))</Culture>
<BootManifestResourceType>satellite</BootManifestResourceType>
<BootManifestResourceName>%(ReferenceSatellitePaths.DestinationSubDirectory)%(FileName)%(Extension)</BootManifestResourceName>
<TargetOutputPath>$(_BlazorRuntimeBinOutputPath)%(ReferenceSatellitePaths.DestinationSubDirectory)%(FileName)%(Extension)</TargetOutputPath>
</_BlazorOutputWithTargetPath>
</ItemGroup>

<!--
We need to know at build time (not publish time) whether or not to include pdbs in the
blazor.boot.json file, so this is controlled by the BlazorEnableDebugging flag, whose
default value is determined by the build configuration.
-->
<ItemGroup Condition="'$(BlazorEnableDebugging)' != 'true'">
<_BlazorOutputWithTargetPath Remove="@(_BlazorOutputWithTargetPath)" Condition="'%(Extension)' == '.pdb'" />
</ItemGroup>

<!--
The following itemgroup attempts to extend the set to include satellite assemblies.
The mechanism behind this (or whether it's correct) is a bit unclear so
https://github.com/dotnet/aspnetcore/issues/18951 tracks the need for follow-up.
-->
<ItemGroup>
<!--
ReferenceCopyLocalPaths includes all files that are part of the build out with CopyLocalLockFileAssemblies on.
Remove assemblies that are inputs to calculating the assembly closure. Instead use the resolved outputs, since it is the minimal set.
-->
<_BlazorCopyLocalPaths Include="@(ReferenceCopyLocalPaths)" Condition="'%(Extension)' == '.dll'" />
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some of this code was duplicated in the file, possibly a bad merge.

<_BlazorCopyLocalPaths Remove="@(_BlazorManagedRuntimeAssemby)" Condition="'%(Extension)' == '.dll'" />

<_BlazorOutputWithTargetPath Include="@(_BlazorCopyLocalPaths)">
<_BlazorOutputWithTargetPath Include="@(_BlazorResolvedAssembly)">
<BootManifestResourceType Condition="'%(Extension)' == '.dll'">assembly</BootManifestResourceType>
<BootManifestResourceType Condition="'%(Extension)' == '.pdb'">pdb</BootManifestResourceType>
<BootManifestResourceType Condition="'%(_BlazorCopyLocalPaths.Extension)' == '.pdb'">pdb</BootManifestResourceType>
<BootManifestResourceName>%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)</BootManifestResourceName>
<TargetOutputPath>$(_BlazorRuntimeBinOutputPath)%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension)</TargetOutputPath>
</_BlazorOutputWithTargetPath>
</ItemGroup>

<ItemGroup>
<_DotNetWasmRuntimeFile Include="$(ComponentsWebAssemblyRuntimePath)*" />
<_BlazorOutputWithTargetPath Include="@(_DotNetWasmRuntimeFile)">
<TargetOutputPath>$(_BlazorRuntimeWasmOutputPath)%(FileName)%(Extension)</TargetOutputPath>
<BootManifestResourceType>runtime</BootManifestResourceType>
<BootManifestResourceName>%(FileName)%(Extension)</BootManifestResourceName>
</_BlazorOutputWithTargetPath>

<_BlazorJSFile Include="$(_BlazorJSPath)" />
<_BlazorJSFile Include="$(_BlazorJSMapPath)" Condition="Exists('$(_BlazorJSMapPath)')" />
<_BlazorOutputWithTargetPath Include="@(_BlazorJSFile)">
<TargetOutputPath>$(_BaseBlazorRuntimeOutputPath)%(FileName)%(Extension)</TargetOutputPath>
</_BlazorOutputWithTargetPath>
</ItemGroup>

<!--
We need to know at build time (not publish time) whether or not to include pdbs in the
blazor.boot.json file, so this is controlled by the BlazorEnableDebugging flag, whose
default value is determined by the build configuration.
-->
<ItemGroup Condition="'$(BlazorEnableDebugging)' != 'true'">
<_BlazorOutputWithTargetPath Remove="@(_BlazorOutputWithTargetPath)" Condition="'%(Extension)' == '.pdb'" />
</ItemGroup>
</Target>

<!--
Expand Down Expand Up @@ -218,7 +213,7 @@
<Target
Name="_LinkBlazorApplication"
Inputs="$(ProjectAssetsFile);
@(_BlazorManagedRuntimeAssemby);
@(_BlazorManagedRuntimeAssembly);
@(BlazorLinkerDescriptor);
$(MSBuildAllProjects)"
Outputs="$(_BlazorLinkerOutputCache)">
Expand Down Expand Up @@ -277,7 +272,7 @@
Name="_ResolveBlazorRuntimeDependencies"
Inputs="$(ProjectAssetsFile);
@(IntermediateAssembly);
@(_BlazorManagedRuntimeAssemby)"
@(_BlazorManagedRuntimeAssembly)"
Outputs="$(_BlazorApplicationAssembliesCacheFile)">

<!--
Expand All @@ -287,7 +282,7 @@
-->
<ResolveBlazorRuntimeDependencies
EntryPoint="@(IntermediateAssembly)"
ApplicationDependencies="@(_BlazorManagedRuntimeAssemby)"
ApplicationDependencies="@(_BlazorManagedRuntimeAssembly)"
WebAssemblyBCLAssemblies="@(_WebAssemblyBCLAssembly)">

<Output TaskParameter="Dependencies" ItemName="_BlazorResolvedAssembly" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<SourceId>$(PackageId)</SourceId>
<ContentRoot>$([MSBuild]::NormalizeDirectory('$(TargetDir)wwwroot\'))</ContentRoot>
<BasePath>$(StaticWebAssetBasePath)</BasePath>
<RelativePath>$([System.String]::Copy('%(_BlazorOutputWithTargetPath.TargetOutputPath)').Replace('\','/').Replace('dist/',''))</RelativePath>
<RelativePath>$([System.String]::Copy('%(_BlazorOutputWithTargetPath.TargetOutputPath)').Replace('\','/'))</RelativePath>
</StaticWebAsset>

<StaticWebAsset Remove="@(StaticWebAsset)" Condition="'$(BlazorEnableDebugging)' != 'true' and '%(SourceType)' == '' and '%(Extension)' == '.pdb'" />
Expand Down
Loading