Skip to content
27 changes: 13 additions & 14 deletions src/mono/browser/runtime/loader/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ export function normalizeConfig () {
config.cachedResourcesPurgeDelay = 10000;
}

if (!config.applicationEnvironment) {
config.applicationEnvironment = "Production";
}

// ActiveIssue https://github.com/dotnet/runtime/issues/75602
if (WasmEnableThreads) {

Expand Down Expand Up @@ -219,6 +223,15 @@ export function normalizeConfig () {
config.environmentVariables!["LANG"] = `${config.applicationCulture}.UTF-8`;
}

if (config.debugLevel !== 0 && globalThis.window?.document?.querySelector("script[src*='aspnetcore-browser-refresh']")) {
if (!config.environmentVariables["DOTNET_MODIFIABLE_ASSEMBLIES"]) {
config.environmentVariables["DOTNET_MODIFIABLE_ASSEMBLIES"] = "debug";
}
if (!config.environmentVariables["__ASPNETCORE_BROWSER_TOOLS"]) {
config.environmentVariables["__ASPNETCORE_BROWSER_TOOLS"] = "true";
}
}

runtimeHelpers.diagnosticTracing = loaderHelpers.diagnosticTracing = !!config.diagnosticTracing;
runtimeHelpers.waitForDebugger = config.waitForDebugger;

Expand Down Expand Up @@ -325,20 +338,6 @@ async function loadBootConfig (module: DotnetModuleInternal): Promise<void> {

deep_merge_config(loaderHelpers.config, loadedConfig);

if (!loaderHelpers.config.applicationEnvironment) {
loaderHelpers.config.applicationEnvironment = "Production";
}

if (loaderHelpers.config.debugLevel !== 0 && globalThis.window?.document?.querySelector("script[src*='aspnetcore-browser-refresh']")) {
loaderHelpers.config.environmentVariables = loaderHelpers.config.environmentVariables || {};
if (!loaderHelpers.config.environmentVariables["DOTNET_MODIFIABLE_ASSEMBLIES"]) {
loaderHelpers.config.environmentVariables["DOTNET_MODIFIABLE_ASSEMBLIES"] = "debug";
}
if (!loaderHelpers.config.environmentVariables["__ASPNETCORE_BROWSER_TOOLS"]) {
loaderHelpers.config.environmentVariables["__ASPNETCORE_BROWSER_TOOLS"] = "true";
}
}

function fetchBootConfig (url: string): Promise<Response> {
return loaderHelpers.fetch_like(url, {
method: "GET",
Expand Down
2 changes: 2 additions & 0 deletions src/mono/browser/runtime/loader/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ const legacyEntrypoint = createEmscripten;

verifyEnvironment();

dotnet.withConfig(/*! dotnetBootConfig */{});
Copy link
Member

Choose a reason for hiding this comment

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

Why do we want to merge monoConfig with empty value?

Copy link
Member Author

@maraf maraf Apr 17, 2025

Choose a reason for hiding this comment

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

This is a placeholder that gets replaced during build by the actual boot config. If the inlining is turned off (temporal option) this will be no-op (merge with empty value).


export { dotnet, exit };
export default legacyEntrypoint;
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ Copyright (c) .NET Foundation. All rights reserved.
<_BlazorWebAssemblyStartupMemoryCache>$(BlazorWebAssemblyStartupMemoryCache)</_BlazorWebAssemblyStartupMemoryCache>
<_BlazorWebAssemblyJiterpreter>$(BlazorWebAssemblyJiterpreter)</_BlazorWebAssemblyJiterpreter>
<_BlazorWebAssemblyRuntimeOptions>$(BlazorWebAssemblyRuntimeOptions)</_BlazorWebAssemblyRuntimeOptions>
<_WasmInlineBootConfig>$(WasmInlineBootConfig)</_WasmInlineBootConfig>
<_WasmInlineBootConfig Condition="'$(_WasmInlineBootConfig)' == '' and '$(_TargetingNET100OrLater)' == 'true' and '$(WasmBootConfigFileName)' == ''">true</_WasmInlineBootConfig>
<_WasmInlineBootConfig Condition="'$(_WasmInlineBootConfig)' == ''">false</_WasmInlineBootConfig>
<!-- true = wasm assets will have hard fingerprint; false = wasm assets won't have even soft fingerprint -->
<_WasmFingerprintAssets>$(WasmFingerprintAssets)</_WasmFingerprintAssets>
<_WasmFingerprintAssets Condition="'$(_WasmFingerprintAssets)' == '' and '$(_TargetingNET90OrLater)' == 'true'">true</_WasmFingerprintAssets>
Expand All @@ -197,10 +200,12 @@ Copyright (c) .NET Foundation. All rights reserved.
<_WasmFingerprintDotnetJs Condition="'$(_WasmFingerprintDotnetJs)' == ''">$(OverrideHtmlAssetPlaceholders)</_WasmFingerprintDotnetJs>
<_WasmFingerprintDotnetJs Condition="'$(_WasmFingerprintDotnetJs)' == ''">false</_WasmFingerprintDotnetJs>
<!-- true = boot config will have hard fingerprint; false = boot config will have soft fingerprint -->
<_WasmFingerprintBootConfig>$(WasmFingerprintBootConfig)</_WasmFingerprintBootConfig>
<_WasmFingerprintBootConfig Condition="'$(_WasmInlineBootConfig)' == 'true'">$(_WasmFingerprintDotnetJs)</_WasmFingerprintBootConfig>
<_WasmFingerprintBootConfig Condition="'$(_WasmFingerprintBootConfig)' == ''">$(WasmFingerprintBootConfig)</_WasmFingerprintBootConfig>
<_WasmFingerprintBootConfig Condition="'$(_WasmFingerprintBootConfig)' == ''">$(OverrideHtmlAssetPlaceholders)</_WasmFingerprintBootConfig>
<_WasmFingerprintBootConfig Condition="'$(_WasmFingerprintBootConfig)' == ''">false</_WasmFingerprintBootConfig>
<_WasmBootConfigFileName>$(WasmBootConfigFileName)</_WasmBootConfigFileName>
<_WasmBootConfigFileName Condition="'$(_WasmBootConfigFileName)' == '' and '$(_TargetingNET100OrLater)' == 'true' and '$(_WasmInlineBootConfig)' == 'true'">dotnet.js</_WasmBootConfigFileName>
<_WasmBootConfigFileName Condition="'$(_WasmBootConfigFileName)' == '' and '$(_TargetingNET100OrLater)' == 'true'">dotnet.boot.js</_WasmBootConfigFileName>
<_WasmBootConfigFileName Condition="'$(_WasmBootConfigFileName)' == ''">blazor.boot.json</_WasmBootConfigFileName>
<_WasmPublishBootConfigFileName>publish.$(_WasmBootConfigFileName)</_WasmPublishBootConfigFileName>
Expand Down Expand Up @@ -259,6 +264,11 @@ Copyright (c) .NET Foundation. All rights reserved.
<Output TaskParameter="FilesToRemove" ItemName="_WasmBuildFilesToRemove" />
</ComputeWasmBuildAssets>

<ItemGroup Condition="'$(_WasmInlineBootConfig)' == 'true'">
<_WasmDotnetJsForBuild Include="@(ReferenceCopyLocalPaths)" Condition="'%(FileName)%(Extension)' == 'dotnet.js'" />
<_BuildAssetsCandidates Remove="@(_WasmDotnetJsForBuild)" />
</ItemGroup>

<PropertyGroup>
<_WasmBuildWebCilPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'webcil'))</_WasmBuildWebCilPath>
<_WasmBuildTmpWebCilPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'tmp-webcil'))</_WasmBuildTmpWebCilPath>
Expand Down Expand Up @@ -376,6 +386,7 @@ Copyright (c) .NET Foundation. All rights reserved.
DebugLevel="$(WasmDebugLevel)"
LinkerEnabled="false"
CacheBootResources="$(BlazorCacheBootResources)"
MergeWith="@(_WasmDotnetJsForBuild)"
OutputPath="$(_WasmBuildBootJsonPath)"
ConfigurationFiles="@(_WasmJsConfigStaticWebAsset)"
LazyLoadedAssemblies="@(BlazorWebAssemblyLazyLoad)"
Expand Down Expand Up @@ -503,6 +514,10 @@ Copyright (c) .NET Foundation. All rights reserved.
<Output TaskParameter="PromotedAssets" ItemName="_PromotedWasmPublishStaticWebAssets" />
<Output TaskParameter="FilesToRemove" ItemName="_PublishResolvedFilesToRemove" />
</ComputeWasmPublishAssets>

<ItemGroup Condition="'$(_WasmInlineBootConfig)' == 'true'">
<_WasmDotnetJsForPublish Include="@(ResolvedFileToPublish)" Condition="'%(FileName)%(Extension)' == 'dotnet.js'" />
</ItemGroup>

<PropertyGroup>
<_WasmPublishWebCilPath>$([MSBuild]::NormalizeDirectory($(IntermediateOutputPath), 'webcil', 'publish'))</_WasmPublishWebCilPath>
Expand Down Expand Up @@ -679,6 +694,7 @@ Copyright (c) .NET Foundation. All rights reserved.
DebugLevel="$(WasmDebugLevel)"
LinkerEnabled="$(PublishTrimmed)"
CacheBootResources="$(BlazorCacheBootResources)"
MergeWith="@(_WasmDotnetJsForPublish)"
OutputPath="$(IntermediateOutputPath)$(_WasmPublishBootConfigFileName)"
ConfigurationFiles="@(_WasmPublishConfigFile)"
LazyLoadedAssemblies="@(BlazorWebAssemblyLazyLoad)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public BuildOptions(
bool WarnAsError = true,
RuntimeVariant RuntimeType = RuntimeVariant.SingleThreaded,
IDictionary<string, string>? ExtraBuildEnvironmentVariables = null,
string BootConfigFileName = "dotnet.boot.js",
string? BootConfigFileName = null,
string NonDefaultFrameworkDir = "",
string ExtraMSBuildArgs = "",
bool WasmPerfTracing = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public abstract record MSBuildOptions
bool WarnAsError = true,
RuntimeVariant RuntimeType = RuntimeVariant.SingleThreaded,
IDictionary<string, string>? ExtraBuildEnvironmentVariables = null,
string BootConfigFileName = "dotnet.boot.js",
string? BootConfigFileName = null,
string NonDefaultFrameworkDir = "",
string ExtraMSBuildArgs = "",
bool WasmPerfTracing = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public PublishOptions(
bool WarnAsError = true,
RuntimeVariant RuntimeType = RuntimeVariant.SingleThreaded,
IDictionary<string, string>? ExtraBuildEnvironmentVariables = null,
string BootConfigFileName = "dotnet.boot.js",
string? BootConfigFileName = null,
string NonDefaultFrameworkDir = "",
string ExtraMSBuildArgs = "",
bool BuildOnlyAfterPublish = true,
Expand Down
7 changes: 5 additions & 2 deletions src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -366,14 +366,17 @@ private string[] GetFilesMatchingNameConsideringFingerprinting(string filePath,
dict[Path.GetFileName(file)] = (file, unchanged);

// those files do not change on re-link
dict["dotnet.js"]=(Path.Combine(paths.BinFrameworkDir, "dotnet.js"), true);
dict["dotnet.js"]=(Path.Combine(paths.BinFrameworkDir, "dotnet.js"), false); // Inline boot config
dict["dotnet.js.map"]=(Path.Combine(paths.BinFrameworkDir, "dotnet.js.map"), true);
dict["dotnet.runtime.js"]=(Path.Combine(paths.BinFrameworkDir, "dotnet.runtime.js"), true);
dict["dotnet.runtime.js.map"]=(Path.Combine(paths.BinFrameworkDir, "dotnet.runtime.js.map"), true);

if (IsFingerprintingEnabled)
{
string bootJsonPath = Path.Combine(paths.BinFrameworkDir, "dotnet.boot.js");
if (!File.Exists(bootJsonPath))
bootJsonPath = Path.Combine(paths.BinFrameworkDir, "dotnet.js"); // inline boot config
Copy link
Member

Choose a reason for hiding this comment

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

Won't this be confusing that we are checking is-config-inlined by the presence of config files, not by WasmInlineBootConfig property? Can it happen that "dotnet.boot.js" won't be produced because of runtime logic error, not because inlining was enabled?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ideally, we should pass MSBuildOptions (or something similar) here as to other places check the boot config file name.
I can make this refactoring in a follow-up.


BootJsonData bootJson = GetBootJson(bootJsonPath);
var keysToUpdate = new List<string>();
var updates = new List<(string oldKey, string newKey, (string fullPath, bool unchanged) value)>();
Expand Down Expand Up @@ -493,7 +496,7 @@ private BootJsonData GetBootJson(string bootJsonPath)
public BootJsonData AssertBootJson(AssertBundleOptions options)
{
EnsureProjectDirIsSet();
string bootJsonPath = Path.Combine(options.BinFrameworkDir, options.BuildOptions.BootConfigFileName);
string bootJsonPath = Path.Combine(options.BinFrameworkDir, options.BuildOptions.BootConfigFileName ?? "dotnet.js");
BootJsonData bootJson = GetBootJson(bootJsonPath);
string spcExpectedFilename = $"System.Private.CoreLib{WasmAssemblyExtension}";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public virtual (string projectDir, string buildOutput) BuildProject(

buildOptions.ExtraBuildEnvironmentVariables["TreatPreviousAsCurrent"] = "false";

if (buildOptions.BootConfigFileName != "dotnet.boot.js")
if (buildOptions.BootConfigFileName != null)
{
// Omit implicit default
buildOptions = buildOptions with { ExtraMSBuildArgs = $"{buildOptions.ExtraMSBuildArgs} -p:WasmBootConfigFileName={buildOptions.BootConfigFileName}" };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected override IReadOnlyDictionary<string, bool> GetAllKnownDotnetFilesToFin
{ "dotnet.diagnostics.js.map", false },
};

if (assertOptions.BuildOptions.BootConfigFileName.EndsWith(".js"))
if (assertOptions.BuildOptions.BootConfigFileName?.EndsWith(".js") ?? false)
result[assertOptions.BuildOptions.BootConfigFileName] = false;

return result;
Expand Down Expand Up @@ -76,7 +76,7 @@ protected override IReadOnlySet<string> GetDotNetFilesExpectedSet(AssertBundleOp
res.Add("dotnet.diagnostics.js.map");
}

if (assertOptions.BuildOptions.BootConfigFileName.EndsWith(".js"))
if (assertOptions.BuildOptions.BootConfigFileName?.EndsWith(".js") ?? false)
res.Add(assertOptions.BuildOptions.BootConfigFileName);

return res;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,23 @@ public bool IsCoreAssembly(string fileName)
return false;
}

public void WriteConfigToFile(BootJsonData config, string outputPath, string? outputFileExtension = null)
public void WriteConfigToFile(BootJsonData config, string outputPath, string? outputFileExtension = null, string? mergeWith = null)
{
var output = JsonSerializer.Serialize(config, JsonOptions);

outputFileExtension ??= Path.GetExtension(outputPath);
Log.LogMessage($"Write config in format '{outputFileExtension}'");
if (outputFileExtension == ".js")
if (mergeWith != null)
{
string existingContent = File.ReadAllText(mergeWith);
output = existingContent.Replace("/*! dotnetBootConfig */{}", $"/*json-start*/{output}/*json-end*/");
if (existingContent.Equals(output))
Log.LogError($"Merging boot config into '{mergeWith}' failed to find the placeholder.");
}
else if (outputFileExtension == ".js")
{
output = $"export const config = /*json-start*/{output}/*json-end*/;";
}

File.WriteAllText(outputPath, output);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ public class GenerateWasmBootJson : Task

public string ApplicationEnvironment { get; set; }

public string MergeWith { get; set; }

public override bool Execute()
{
var entryAssemblyName = AssemblyName.GetAssemblyName(AssemblyPath).Name;
Expand Down Expand Up @@ -442,7 +444,7 @@ private void WriteBootConfig(string entryAssemblyName)
}

helper.ComputeResourcesHash(result);
helper.WriteConfigToFile(result, OutputPath);
helper.WriteConfigToFile(result, OutputPath, mergeWith: MergeWith);

void AddResourceToList(ITaskItem resource, ResourceHashesByNameDictionary resourceList, string resourceKey)
{
Expand Down
Loading