Skip to content

Commit 2637fda

Browse files
committed
Pass DOTNET_MODIFIABLE_ASSEMBLIES & __ASPNETCORE_BROWSER_TOOLS in WebAssembly options comment
1 parent 014165a commit 2637fda

File tree

11 files changed

+93
-103
lines changed

11 files changed

+93
-103
lines changed

src/Components/Endpoints/src/DependencyInjection/RazorComponentsServiceCollectionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public static IRazorComponentsBuilder AddRazorComponents(this IServiceCollection
7171
services.TryAddScoped<AuthenticationStateProvider, ServerAuthenticationStateProvider>();
7272
services.AddSupplyValueFromQueryProvider();
7373
services.TryAddCascadingValue(sp => sp.GetRequiredService<EndpointHtmlRenderer>().HttpContext);
74-
services.TryAddScoped<WebAssemblyEnvironmentEmitter>();
74+
services.TryAddScoped<WebAssemblySettingsEmitter>();
7575

7676
services.TryAddScoped<ResourceCollectionProvider>();
7777

src/Components/Endpoints/src/DependencyInjection/WebAssemblyEnvironmentEmitter.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
using Microsoft.Extensions.Hosting;
6+
7+
namespace Microsoft.AspNetCore.Components.Endpoints;
8+
9+
internal record WebAssemblySettings(string EnvironmentName, Dictionary<string, string> EnvironmentVariables);
10+
11+
internal class WebAssemblySettingsEmitter(IHostEnvironment hostEnvironment)
12+
{
13+
private bool wasEmittedAlready;
14+
15+
private const string dotnetModifiableAssembliesName = "DOTNET_MODIFIABLE_ASSEMBLIES";
16+
private const string aspnetcoreBrowserToolsName = "__ASPNETCORE_BROWSER_TOOLS";
17+
18+
private static readonly string? s_dotnetModifiableAssemblies = GetNonEmptyEnvironmentVariableValue(dotnetModifiableAssembliesName);
19+
private static readonly string? s_aspnetcoreBrowserTools = GetNonEmptyEnvironmentVariableValue(aspnetcoreBrowserToolsName);
20+
21+
private static string? GetNonEmptyEnvironmentVariableValue(string name)
22+
=> Environment.GetEnvironmentVariable(name) is { Length: > 0 } value ? value : null;
23+
24+
public bool TryGetSettingsOnce([NotNullWhen(true)] out WebAssemblySettings? settings)
25+
{
26+
if (wasEmittedAlready)
27+
{
28+
settings = default;
29+
return false;
30+
}
31+
32+
var environmentVariables = new Dictionary<string, string>();
33+
34+
// DOTNET_MODIFIABLE_ASSEMBLIES is used by the runtime to initialize hot-reload specific environment variables and is configured
35+
// by the launching process (dotnet-watch / Visual Studio).
36+
// Always add the header if the environment variable is set, regardless of the kind of environment.
37+
if (s_dotnetModifiableAssemblies != null)
38+
{
39+
environmentVariables[dotnetModifiableAssembliesName] = s_dotnetModifiableAssemblies;
40+
}
41+
42+
// See https://github.com/dotnet/aspnetcore/issues/37357#issuecomment-941237000
43+
// Translate the _ASPNETCORE_BROWSER_TOOLS environment configured by the browser tools agent in to a HTTP response header.
44+
if (s_aspnetcoreBrowserTools != null)
45+
{
46+
environmentVariables[aspnetcoreBrowserToolsName] = s_aspnetcoreBrowserTools;
47+
}
48+
49+
wasEmittedAlready = true;
50+
settings = new (hostEnvironment.EnvironmentName, environmentVariables);
51+
return true;
52+
}
53+
}

src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Streaming.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,9 +273,10 @@ private void WriteComponentHtml(int componentId, TextWriter output, bool allowBo
273273

274274
if (marker.Type is ComponentMarker.WebAssemblyMarkerType or ComponentMarker.AutoMarkerType)
275275
{
276-
if (_httpContext.RequestServices.GetRequiredService<WebAssemblyEnvironmentEmitter>().TryGetEnvironmentOnce(out var environment))
276+
if (_httpContext.RequestServices.GetRequiredService<WebAssemblySettingsEmitter>().TryGetSettingsOnce(out var settings))
277277
{
278-
output.Write($"<!--Blazor-WebAssemblyEnvironment:{environment}-->");
278+
var settingsJson = JsonSerializer.Serialize(settings, ServerComponentSerializationSettings.JsonSerializationOptions);
279+
output.Write($"<!--Blazor-WebAssembly:{settingsJson}-->");
279280
}
280281
}
281282

src/Components/Web.JS/src/Boot.WebAssembly.Common.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRen
1111
import { Pointer } from './Platform/Platform';
1212
import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions';
1313
import { addDispatchEventMiddleware } from './Rendering/WebRendererInteropMethods';
14-
import { WebAssemblyComponentDescriptor, discoverWebAssemblyPersistedState } from './Services/ComponentDescriptorDiscovery';
14+
import { WebAssemblyComponentDescriptor, WebAssemblyServerOptions, discoverWebAssemblyPersistedState } from './Services/ComponentDescriptorDiscovery';
1515
import { receiveDotNetDataStream } from './StreamingInterop';
1616
import { WebAssemblyComponentAttacher } from './Platform/WebAssemblyComponentAttacher';
1717
import { MonoConfig } from '@microsoft/dotnet-runtime';
@@ -68,23 +68,23 @@ export function setWebAssemblyOptions(initializersReady: Promise<Partial<WebAsse
6868
}
6969
}
7070

71-
export function startWebAssembly(components: RootComponentManager<WebAssemblyComponentDescriptor>, environment: string | null | undefined): Promise<void> {
71+
export function startWebAssembly(components: RootComponentManager<WebAssemblyComponentDescriptor>, options: WebAssemblyServerOptions | undefined): Promise<void> {
7272
if (startPromise !== undefined) {
7373
throw new Error('Blazor WebAssembly has already started.');
7474
}
7575

76-
startPromise = new Promise(startCore.bind(null, components, environment));
76+
startPromise = new Promise(startCore.bind(null, components, options));
7777

7878
return startPromise;
7979
}
8080

81-
async function startCore(components: RootComponentManager<WebAssemblyComponentDescriptor>, environment: string | null | undefined, resolve, _) {
81+
async function startCore(components: RootComponentManager<WebAssemblyComponentDescriptor>, options: WebAssemblyServerOptions | undefined, resolve, _) {
8282
if (inAuthRedirectIframe()) {
8383
// eslint-disable-next-line @typescript-eslint/no-empty-function
8484
await new Promise(() => { }); // See inAuthRedirectIframe for explanation
8585
}
8686

87-
const platformLoadPromise = loadWebAssemblyPlatformIfNotStarted(environment);
87+
const platformLoadPromise = loadWebAssemblyPlatformIfNotStarted(options);
8888

8989
addDispatchEventMiddleware((browserRendererId, eventHandlerId, continuation) => {
9090
// It's extremely unusual, but an event can be raised while we're in the middle of synchronously applying a
@@ -206,16 +206,19 @@ export function waitForBootConfigLoaded(): Promise<MonoConfig> {
206206
return bootConfigPromise;
207207
}
208208

209-
export function loadWebAssemblyPlatformIfNotStarted(environment: string | null | undefined): Promise<void> {
209+
export function loadWebAssemblyPlatformIfNotStarted(serverOptions: WebAssemblyServerOptions | undefined): Promise<void> {
210210
platformLoadPromise ??= (async () => {
211211
await initializersPromise;
212212
const finalOptions = options ?? {};
213213
if (!finalOptions.environment) {
214-
finalOptions.environment = environment ?? undefined;
214+
finalOptions.environment = serverOptions?.environmentName ?? undefined;
215215
}
216216
const existingConfig = options?.configureRuntime;
217217
finalOptions.configureRuntime = (config) => {
218218
existingConfig?.(config);
219+
if (serverOptions?.environmentVariables) {
220+
config.withEnvironmentVariables(serverOptions.environmentVariables);
221+
}
219222
if (waitForRootComponents) {
220223
config.withEnvironmentVariable('__BLAZOR_WEBASSEMBLY_WAIT_FOR_ROOT_COMPONENTS', 'true');
221224
}

src/Components/Web.JS/src/Boot.WebAssembly.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Blazor } from './GlobalExports';
66
import { shouldAutoStart } from './BootCommon';
77
import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions';
88
import { setWebAssemblyOptions, startWebAssembly } from './Boot.WebAssembly.Common';
9-
import { WebAssemblyComponentDescriptor, discoverComponents, discoverWebAssemblyEnvironment } from './Services/ComponentDescriptorDiscovery';
9+
import { WebAssemblyComponentDescriptor, discoverComponents, discoverWebAssemblyOptions } from './Services/ComponentDescriptorDiscovery';
1010
import { DotNet } from '@microsoft/dotnet-js-interop';
1111
import { InitialRootComponentsList } from './Services/InitialRootComponentsList';
1212
import { JSEventRegistry } from './Services/JSEventRegistry';
@@ -24,10 +24,10 @@ async function boot(options?: Partial<WebAssemblyStartOptions>): Promise<void> {
2424

2525
JSEventRegistry.create(Blazor);
2626
const webAssemblyComponents = discoverComponents(document, 'webassembly') as WebAssemblyComponentDescriptor[];
27-
const webAssemblyEnvironment = discoverWebAssemblyEnvironment(document);
27+
const webAssemblyOptions = discoverWebAssemblyOptions(document);
2828

2929
const components = new InitialRootComponentsList(webAssemblyComponents);
30-
await startWebAssembly(components, webAssemblyEnvironment);
30+
await startWebAssembly(components, webAssemblyOptions);
3131
}
3232

3333
Blazor.start = boot;

src/Components/Web.JS/src/Rendering/DomMerging/DomSync.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
import { AutoComponentDescriptor, ComponentDescriptor, ServerComponentDescriptor, WebAssemblyComponentDescriptor, canMergeDescriptors, discoverComponents, discoverWebAssemblyEnvironment, mergeDescriptors } from '../../Services/ComponentDescriptorDiscovery';
4+
import { AutoComponentDescriptor, ComponentDescriptor, ServerComponentDescriptor, WebAssemblyComponentDescriptor, WebAssemblyServerOptions, canMergeDescriptors, discoverComponents, discoverWebAssemblyOptions, mergeDescriptors } from '../../Services/ComponentDescriptorDiscovery';
55
import { isInteractiveRootComponentElement } from '../BrowserRenderer';
66
import { applyAnyDeferredValue } from '../DomSpecialPropertyUtil';
77
import { LogicalElement, getLogicalChildrenArray, getLogicalNextSibling, getLogicalParent, getLogicalRootDescriptor, insertLogicalChild, insertLogicalChildBefore, isLogicalElement, toLogicalElement, toLogicalRootCommentElement } from '../LogicalElements';
@@ -13,7 +13,7 @@ let descriptorHandler: DescriptorHandler | null = null;
1313

1414
export interface DescriptorHandler {
1515
registerComponent(descriptor: ComponentDescriptor): void;
16-
setWebAssemblyEnvironment(webAssemblyEnvironment: string | null | undefined): void;
16+
setWebAssemblyOptions(options: WebAssemblyServerOptions | undefined): void;
1717
}
1818

1919
export function attachComponentDescriptorHandler(handler: DescriptorHandler) {
@@ -22,8 +22,8 @@ export function attachComponentDescriptorHandler(handler: DescriptorHandler) {
2222

2323
export function registerAllComponentDescriptors(root: Node) {
2424
const descriptors = upgradeComponentCommentsToLogicalRootComments(root);
25-
const webAssemblyEnvironment = discoverWebAssemblyEnvironment(root);
26-
descriptorHandler?.setWebAssemblyEnvironment(webAssemblyEnvironment);
25+
const webAssemblyOptions = discoverWebAssemblyOptions(root);
26+
descriptorHandler?.setWebAssemblyOptions(webAssemblyOptions);
2727

2828
for (const descriptor of descriptors) {
2929
descriptorHandler?.registerComponent(descriptor);

src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@ export function discoverComponents(root: Node, type: 'webassembly' | 'server' |
1515
const blazorServerStateCommentRegularExpression = /^\s*Blazor-Server-Component-State:(?<state>[a-zA-Z0-9+/=]+)$/;
1616
const blazorWebAssemblyStateCommentRegularExpression = /^\s*Blazor-WebAssembly-Component-State:(?<state>[a-zA-Z0-9+/=]+)$/;
1717
const blazorWebInitializerCommentRegularExpression = /^\s*Blazor-Web-Initializers:(?<initializers>[a-zA-Z0-9+/=]+)$/;
18-
const blazorWebAssemblyEnvironmentCommentRegularExpression = /^\s*Blazor-WebAssemblyEnvironment:(?<environment>[a-zA-Z0-9+/=]+)$/;
18+
const blazorWebAssemblyOptionsCommentRegularExpression = /^\s*Blazor-WebAssembly:[^{]*(?<options>.*)$/;
1919

20-
export function discoverWebAssemblyEnvironment(root: Node): string | null | undefined {
21-
return discoverBlazorComment(root, blazorWebAssemblyEnvironmentCommentRegularExpression, 'environment');
20+
export function discoverWebAssemblyOptions(root: Node): WebAssemblyServerOptions | undefined {
21+
const optionsJson = discoverBlazorComment(root, blazorWebAssemblyOptionsCommentRegularExpression, 'options');
22+
if (!optionsJson) {
23+
return undefined;
24+
}
25+
const options = JSON.parse(optionsJson);
26+
return options;
2227
}
2328

2429
export function discoverServerPersistedState(node: Node): string | null | undefined {
@@ -344,6 +349,11 @@ export type ServerComponentDescriptor = ServerComponentMarker & DescriptorData;
344349
export type WebAssemblyComponentDescriptor = WebAssemblyComponentMarker & DescriptorData;
345350
export type AutoComponentDescriptor = AutoComponentMarker & DescriptorData;
346351

352+
export type WebAssemblyServerOptions = {
353+
environmentName: string,
354+
environmentVariables: { [i: string]: string; }
355+
};
356+
347357
type DescriptorData = {
348358
uniqueId: number;
349359
start: Comment;

src/Components/Web.JS/src/Services/WebRootComponentManager.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
import { ComponentDescriptor, ComponentMarker, descriptorToMarker } from './ComponentDescriptorDiscovery';
4+
import { ComponentDescriptor, ComponentMarker, descriptorToMarker, WebAssemblyServerOptions } from './ComponentDescriptorDiscovery';
55
import { isRendererAttached, registerRendererAttachedListener } from '../Rendering/WebRendererInteropMethods';
66
import { WebRendererId } from '../Rendering/WebRendererId';
77
import { DescriptorHandler } from '../Rendering/DomMerging/DomSync';
@@ -63,7 +63,7 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
6363

6464
private _circuitInactivityTimeoutId: any;
6565

66-
private _webAssemblyEnvironment: string | undefined;
66+
private _webAssemblyOptions: WebAssemblyServerOptions | undefined;
6767

6868
// Implements RootComponentManager.
6969
// An empty array becuase all root components managed
@@ -96,8 +96,8 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
9696
this.rootComponentsMayRequireRefresh();
9797
}
9898

99-
public setWebAssemblyEnvironment(webAssemblyEnvironment: string | undefined): void {
100-
this._webAssemblyEnvironment = webAssemblyEnvironment;
99+
public setWebAssemblyOptions(webAssemblyOptions: WebAssemblyServerOptions | undefined): void {
100+
this._webAssemblyOptions = webAssemblyOptions;
101101
}
102102

103103
public registerComponent(descriptor: ComponentDescriptor) {
@@ -138,7 +138,7 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
138138

139139
setWaitForRootComponents();
140140

141-
const loadWebAssemblyPromise = loadWebAssemblyPlatformIfNotStarted(this._webAssemblyEnvironment);
141+
const loadWebAssemblyPromise = loadWebAssemblyPlatformIfNotStarted(this._webAssemblyOptions);
142142
const bootConfig = await waitForBootConfigLoaded();
143143

144144
if (maxParallelDownloadsOverride !== undefined) {
@@ -188,7 +188,7 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
188188
this.startLoadingWebAssemblyIfNotStarted();
189189

190190
if (!hasStartedWebAssembly()) {
191-
await startWebAssembly(this, this._webAssemblyEnvironment);
191+
await startWebAssembly(this, this._webAssemblyOptions);
192192
}
193193
}
194194

src/Components/WebAssembly/Server/src/Builder/WebAssemblyRazorComponentsEndpointConventionBuilderExtensions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ public static RazorComponentsEndpointConventionBuilder AddInteractiveWebAssembly
5959
var descriptors = StaticAssetsEndpointDataSourceHelper.ResolveStaticAssetDescriptors(endpointBuilder, options.StaticAssetsManifestPath);
6060
if (descriptors != null && descriptors.Count > 0)
6161
{
62-
ComponentWebAssemblyConventions.AddBlazorWebAssemblyConventions(descriptors, environment);
6362
return builder;
6463
}
6564

0 commit comments

Comments
 (0)