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

[browser] Load appsettings to VFS #85520

Merged
merged 12 commits into from
May 16, 2023
57 changes: 57 additions & 0 deletions src/mono/wasm/Wasm.Build.Tests/Blazor/AppsettingsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.IO;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;

#nullable enable

namespace Wasm.Build.Tests.Blazor;

public class AppsettingsTests : BuildTestBase
{
public AppsettingsTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
: base(output, buildContext)
{
_enablePerTestCleanup = true;
}

[Fact]
public async Task FileInVfs()
{
string id = $"blazor_{Path.GetRandomFileName()}";
string projectFile = CreateWasmTemplateProject(id, "blazorwasm");

string projectDirectory = Path.GetDirectoryName(projectFile)!;

File.WriteAllText(Path.Combine(projectDirectory, "wwwroot", "appsettings.json"), $"{{ \"Id\": \"{id}\" }}");

string programPath = Path.Combine(projectDirectory, "Program.cs");
string programContent = File.ReadAllText(programPath);
programContent = programContent.Replace("var builder",
"""
System.Console.WriteLine($"appSettings Exists '{File.Exists("/appsettings.json")}'");
System.Console.WriteLine($"appSettings Content '{File.ReadAllText("/appsettings.json")}'");
var builder
""");
File.WriteAllText(programPath, programContent);

BlazorBuild(new BlazorBuildOptions(id, "debug", NativeFilesType.FromRuntimePack));

bool existsChecked = false;
bool contentChecked = false;

await BlazorRunForBuildWithDotnetRun("debug", onConsoleMessage: msg =>
{
if (msg.Text.Contains("appSettings Exists 'True'"))
existsChecked = true;
else if (msg.Text.Contains($"appSettings Content '{{ \"Id\": \"{id}\" }}'"))
contentChecked = true;
});

Assert.True(existsChecked, "File '/appsettings.json' wasn't found");
Assert.True(contentChecked, "Content of '/appsettings.json' is not matched");
}
}
13 changes: 8 additions & 5 deletions src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ protected void AssertBlazorBundle(string config, bool isPublish, bool dotnetWasm
same: dotnetWasmFromRuntimePack);
}

protected void AssertBlazorBootJson(string config, bool isPublish, bool isNet7AndBelow, string targetFramework = DefaultTargetFrameworkForBlazor, string? binFrameworkDir=null)
protected void AssertBlazorBootJson(string config, bool isPublish, bool isNet7AndBelow, string targetFramework = DefaultTargetFrameworkForBlazor, string? binFrameworkDir = null)
{
binFrameworkDir ??= FindBlazorBinFrameworkDir(config, isPublish, targetFramework);

Expand Down Expand Up @@ -932,17 +932,18 @@ public void BlazorAddRazorButton(string buttonText, string customCode, string me

// Keeping these methods with explicit Build/Publish in the name
// so in the test code it is evident which is being run!
public Task BlazorRunForBuildWithDotnetRun(string config, Func<IPage, Task>? test = null, string extraArgs = "--no-build")
=> BlazorRunTest($"run -c {config} {extraArgs}", _projectDir!, test);
public Task BlazorRunForBuildWithDotnetRun(string config, Func<IPage, Task>? test = null, string extraArgs = "--no-build", Action<IConsoleMessage>? onConsoleMessage = null)
=> BlazorRunTest($"run -c {config} {extraArgs}", _projectDir!, test, onConsoleMessage);

public Task BlazorRunForPublishWithWebServer(string config, Func<IPage, Task>? test = null, string extraArgs = "")
public Task BlazorRunForPublishWithWebServer(string config, Func<IPage, Task>? test = null, string extraArgs = "", Action<IConsoleMessage>? onConsoleMessage = null)
=> BlazorRunTest($"{s_xharnessRunnerCommand} wasm webserver --app=. --web-server-use-default-files {extraArgs}",
Path.GetFullPath(Path.Combine(FindBlazorBinFrameworkDir(config, forPublish: true), "..")),
test);
test, onConsoleMessage);

public async Task BlazorRunTest(string runArgs,
string workingDirectory,
Func<IPage, Task>? test = null,
Action<IConsoleMessage>? onConsoleMessage = null,
bool detectRuntimeFailures = true)
{
using var runCommand = new RunCommand(s_buildEnv, _testOutput)
Expand All @@ -968,6 +969,8 @@ void OnConsoleMessage(IConsoleMessage msg)
Console.WriteLine($"[{msg.Type}] {msg.Text}");
_testOutput.WriteLine($"[{msg.Type}] {msg.Text}");

onConsoleMessage?.Invoke(msg);

if (detectRuntimeFailures)
{
if (msg.Text.Contains("[MONO] * Assertion") || msg.Text.Contains("Error: [MONO] "))
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/runtime/loader/blazor/BootConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { BootJsonData } from "../../types/blazor";
import type { WebAssemblyBootResourceType } from "../../types";
import { loaderHelpers } from "../globals";

type LoadBootResourceCallback = (type: WebAssemblyBootResourceType, name: string, defaultUri: string, integrity: string) => string | Promise<Response> | null | undefined;
export type LoadBootResourceCallback = (type: WebAssemblyBootResourceType, name: string, defaultUri: string, integrity: string) => string | Promise<Response> | null | undefined;

export class BootConfigResult {
private constructor(public bootConfig: BootJsonData, public applicationEnvironment: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type { BootJsonData, ResourceList } from "../../types/blazor";
import { toAbsoluteUri } from "./_Polyfill";
const networkFetchCacheMode = "no-cache";

const cacheSkipResourceTypes = ["configuration"];

export class WebAssemblyResourceLoader {
private usedCacheKeys: { [key: string]: boolean } = {};

Expand All @@ -27,7 +29,7 @@ export class WebAssemblyResourceLoader {
}

loadResource(name: string, url: string, contentHash: string, resourceType: WebAssemblyBootResourceType): LoadingResource {
const response = this.cacheIfUsed
const response = this.cacheIfUsed && !cacheSkipResourceTypes.includes(resourceType)
? this.loadResourceWithCaching(this.cacheIfUsed, name, url, contentHash, resourceType)
: this.loadResourceWithoutCaching(name, url, contentHash, resourceType);

Expand Down Expand Up @@ -127,10 +129,19 @@ export class WebAssemblyResourceLoader {
// Note that if cacheBootResources was explicitly disabled, we also bypass hash checking
// This is to give developers an easy opt-out from the entire caching/validation flow if
// there's anything they don't like about it.
return fetch(url, {
cache: networkFetchCacheMode,
integrity: this.bootConfig.cacheBootResources ? contentHash : undefined,
});
const fetchOptions: RequestInit = {
cache: networkFetchCacheMode
};

if (resourceType === "configuration") {
maraf marked this conversation as resolved.
Show resolved Hide resolved
// Include credentials so the server can allow download / provide user specific file
fetchOptions.credentials = "include";
} else {
// Any other resource than configuration should provide integrity check
fetchOptions.integrity = this.bootConfig.cacheBootResources ? contentHash : undefined;
}

return fetch(url, fetchOptions);
}

private async addToCacheAsync(cache: Cache, name: string, cacheKey: string, response: Response) {
Expand Down
29 changes: 19 additions & 10 deletions src/mono/wasm/runtime/loader/blazor/_Integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export async function loadBootConfig(config: MonoConfigInternal, module: DotnetM
}

export async function initializeBootConfig(bootConfigResult: BootConfigResult, module: DotnetModuleInternal, startupOptions?: Partial<WebAssemblyStartOptions>) {
INTERNAL.resourceLoader = resourceLoader = await WebAssemblyResourceLoader.initAsync(bootConfigResult.bootConfig, startupOptions || {});
INTERNAL.resourceLoader = resourceLoader = await WebAssemblyResourceLoader.initAsync(bootConfigResult.bootConfig, startupOptions ?? {});
mapBootConfigToMonoConfig(loaderHelpers.config, bootConfigResult.applicationEnvironment);
setupModuleForBlazor(module);
}
Expand All @@ -31,21 +31,20 @@ let resourcesLoaded = 0;
let totalResources = 0;

const behaviorByName = (name: string): AssetBehaviours | "other" => {
return name === "dotnet.timezones.blat" ? "vfs"
: name === "dotnet.native.wasm" ? "dotnetwasm"
: (name.startsWith("dotnet.native.worker") && name.endsWith(".js")) ? "js-module-threads"
: (name.startsWith("dotnet.native") && name.endsWith(".js")) ? "js-module-native"
: (name.startsWith("dotnet.runtime") && name.endsWith(".js")) ? "js-module-runtime"
: (name.startsWith("dotnet") && name.endsWith(".js")) ? "js-module-dotnet"
: name.startsWith("icudt") ? "icu"
: "other";
return name === "dotnet.native.wasm" ? "dotnetwasm"
: (name.startsWith("dotnet.native.worker") && name.endsWith(".js")) ? "js-module-threads"
: (name.startsWith("dotnet.native") && name.endsWith(".js")) ? "js-module-native"
: (name.startsWith("dotnet.runtime") && name.endsWith(".js")) ? "js-module-runtime"
: (name.startsWith("dotnet") && name.endsWith(".js")) ? "js-module-dotnet"
: name.startsWith("icudt") ? "icu"
: "other";
};

const monoToBlazorAssetTypeMap: { [key: string]: WebAssemblyBootResourceType | undefined } = {
"assembly": "assembly",
"pdb": "pdb",
"icu": "globalization",
"vfs": "globalization",
"vfs": "configuration",
maraf marked this conversation as resolved.
Show resolved Hide resolved
"dotnetwasm": "dotnetwasm",
};

Expand Down Expand Up @@ -161,6 +160,16 @@ export function mapBootConfigToMonoConfig(moduleConfig: MonoConfigInternal, appl
};
assets.push(asset);
}
for (let i = 0; i < resourceLoader.bootConfig.config.length; i++) {
const config = resourceLoader.bootConfig.config[i];
if (config === "appsettings.json" || config === `appsettings.${applicationEnvironment}.json`) {
assets.push({
name: config,
resolvedUrl: config,
behavior: "vfs",
});
}
}

if (!hasIcuData) {
moduleConfig.globalizationMode = "invariant";
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/runtime/types/blazor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ export enum ICUDataMode {
All = 1,
Invariant = 2,
Custom = 3
}
}