Skip to content

Commit 8ef1934

Browse files
Blazor Web project interactive options improvements. Rename render modes. (#50684)
Fixes #50433 (Add root level interactivity option) Fixes #50646 (Remove workaround for Counter component) Fixes #50636 (Clarify the names of the interactive render modes) In terms of the code we now emit, there should be nothing controversial here. The template just has to do quite a bit of if/else in many places to account for all these options and how rendermodes are used and not used based on them. The PR is big because the renames have really wide impact, but almost all the "files changes" are just due to renames. The only real code changes are in the project templates. # Testing impact Adding this option, the BlazorWeb template now has **so many** possible combinations of options, including: - Whether or not to enable Server interactivity - Whether or not to enable WebAssembly interactivity - Whether or not to be interactive from the root - Whether or not to include sample content - Whether or not to use ProgramMain So that's around 32 combinations of output - without even accounting for auth options! We don't currently have E2E verification of any of them, as those tests are skipped due to unreliability. We're going to have to lean hard on CTI validations for this, and make sure all the important combinations are covered - cc @mkArtakMSFT. # Options design update Having a list of 6 separate checkboxes in VS is pretty unpleasant and hard to understand: <img src="https://github.com/dotnet/aspnetcore/assets/1101362/93713e83-0793-4140-82e1-95ca63580e3d" width="500" /> So, in this PR I'm proposing (and have implemented, but we can still change it), a change to use dropdowns for the interactivity type and location options. This reduces the number of inputs by one, and means they can be more self-describing: <img src="https://github.com/dotnet/aspnetcore/assets/1101362/649c93fd-d464-499c-b1f2-36436ebf4e3c" width="500" /> * The "interactivity type" choices are: * **None** * **Server** (default) * **WebAssembly** * **Auto (Server and WebAssembly)**. * The "interactivity location" choices are: * **Per page/component** (default) * **Global** Note that "interactivity location" is disabled if interactivity type == "None", but [only CLI honors that right now](dotnet/templating#5648) (VS should add support later, and until then, location will have no effect if there's no interactivity). I think this is much easier to understand, since you no longer have to infer that enabling both Server and WebAssembly means you're going to get Auto. It's also much better in the CLI, since it was completely ridiculous before that `--use-server` defaulted to true but `--use-wasm` defaulted to false, so to get WebAssembly you needed to set `--use-server false --use wasm`. Now you would say `--interactivity webassembly` (and not `wasm` - that was weird too). ![image](https://github.com/dotnet/aspnetcore/assets/1101362/0b4751ad-f91b-4bac-8edf-9e31aa761fbf)
1 parent 20e0a78 commit 8ef1934

File tree

76 files changed

+725
-345
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+725
-345
lines changed

src/Components/Endpoints/src/Builder/RazorComponentDataSourceOptions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ internal class RazorComponentDataSourceOptions
1717
.Create(
1818
equals: (x, y) => (x,y) switch
1919
{
20-
(ServerRenderMode, ServerRenderMode) => true,
21-
(WebAssemblyRenderMode, WebAssemblyRenderMode) => true,
20+
(InteractiveServerRenderMode, InteractiveServerRenderMode) => true,
21+
(InteractiveWebAssemblyRenderMode, InteractiveWebAssemblyRenderMode) => true,
2222
_ => false,
2323
},
2424
getHashCode: obj => obj switch
2525
{
26-
ServerRenderMode => 1,
27-
WebAssemblyRenderMode => 2,
26+
InteractiveServerRenderMode => 1,
27+
InteractiveWebAssemblyRenderMode => 2,
2828
_ => throw new InvalidOperationException($"Unknown render mode: {obj}"),
2929
});
3030

src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ private void UpdateEndpoints()
129129
if (!found)
130130
{
131131
throw new InvalidOperationException($"Unable to find a provider for the render mode: {renderMode.GetType().FullName}. This generally " +
132-
"means that a call to 'AddWebAssemblyComponents' or 'AddServerComponents' is missing. " +
133-
"For example, change builder.Services.AddRazorComponents() to builder.Services.AddRazorComponents().AddServerComponents().");
132+
"means that a call to 'AddInteractiveWebAssemblyComponents' or 'AddInteractiveServerComponents' is missing. " +
133+
"For example, change builder.Services.AddRazorComponents() to builder.Services.AddRazorComponents().AddInteractiveServerComponents().");
134134
}
135135
}
136136

src/Components/Endpoints/src/Discovery/ComponentInfo.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,20 +51,20 @@ private string GetDebuggerDisplay()
5151

5252
private string GetRenderMode()
5353
{
54-
if (RenderMode is ServerRenderMode { Prerender: var server })
54+
if (RenderMode is InteractiveServerRenderMode { Prerender: var server })
5555
{
56-
var size = (nameof(ServerRenderMode).Length - "RenderModeComparer".Length);
57-
return $"RenderModeComparer = {nameof(ServerRenderMode)[0..size]}, Prerendered = {server}";
56+
var size = (nameof(InteractiveServerRenderMode).Length - "RenderModeComparer".Length);
57+
return $"RenderModeComparer = {nameof(InteractiveServerRenderMode)[0..size]}, Prerendered = {server}";
5858
}
59-
if (RenderMode is WebAssemblyRenderMode { Prerender: var wasm })
59+
if (RenderMode is InteractiveWebAssemblyRenderMode { Prerender: var wasm })
6060
{
61-
var size = (nameof(WebAssemblyRenderMode).Length - "RenderModeComparer".Length);
62-
return $"RenderModeComparer = {nameof(WebAssemblyRenderMode)[0..size]}, Prerendered = {wasm}";
61+
var size = (nameof(InteractiveWebAssemblyRenderMode).Length - "RenderModeComparer".Length);
62+
return $"RenderModeComparer = {nameof(InteractiveWebAssemblyRenderMode)[0..size]}, Prerendered = {wasm}";
6363
}
64-
if (RenderMode is AutoRenderMode { Prerender: var auto })
64+
if (RenderMode is InteractiveAutoRenderMode { Prerender: var auto })
6565
{
66-
var size = (nameof(AutoRenderMode).Length - "RenderModeComparer".Length);
67-
return $"RenderModeComparer = {nameof(AutoRenderMode)[0..size]}, Prerendered = {auto}";
66+
var size = (nameof(InteractiveAutoRenderMode).Length - "RenderModeComparer".Length);
67+
return $"RenderModeComparer = {nameof(InteractiveAutoRenderMode)[0..size]}, Prerendered = {auto}";
6868
}
6969

7070
return "RenderModeComparer = Unknown, Prerendered = Unknown";

src/Components/Endpoints/src/Discovery/RazorComponentApplication.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ public ISet<IComponentRenderMode> GetDeclaredRenderModesByDiscoveredComponents()
4242
var component = Components[i];
4343
switch (component.RenderMode)
4444
{
45-
case ServerRenderMode:
46-
set.Add(RenderMode.Server);
45+
case InteractiveServerRenderMode:
46+
set.Add(RenderMode.InteractiveServer);
4747
break;
48-
case WebAssemblyRenderMode:
49-
set.Add(RenderMode.WebAssembly);
48+
case InteractiveWebAssemblyRenderMode:
49+
set.Add(RenderMode.InteractiveWebAssembly);
5050
break;
51-
case AutoRenderMode:
52-
set.Add(RenderMode.Server);
53-
set.Add(RenderMode.WebAssembly);
51+
case InteractiveAutoRenderMode:
52+
set.Add(RenderMode.InteractiveServer);
53+
set.Add(RenderMode.InteractiveWebAssembly);
5454
break;
5555
default:
5656
break;

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ internal static void UpdateSaveStateRenderMode(HttpContext httpContext, ICompone
6565
{
6666
var currentInvocation = mode switch
6767
{
68-
ServerRenderMode => InvokedRenderModes.Mode.Server,
69-
WebAssemblyRenderMode => InvokedRenderModes.Mode.WebAssembly,
70-
AutoRenderMode => throw new NotImplementedException("TODO: To be able to support AutoRenderMode, we have to serialize persisted state in both WebAssembly and Server formats, or unify the two formats."),
68+
InteractiveServerRenderMode => InvokedRenderModes.Mode.Server,
69+
InteractiveWebAssemblyRenderMode => InvokedRenderModes.Mode.WebAssembly,
70+
InteractiveAutoRenderMode => throw new NotImplementedException("TODO: To be able to support InteractiveAutoRenderMode, we have to serialize persisted state in both WebAssembly and Server formats, or unify the two formats."),
7171
_ => throw new ArgumentException(Resources.FormatUnsupportedRenderMode(mode), nameof(mode)),
7272
};
7373

@@ -88,9 +88,9 @@ internal static void UpdateSaveStateRenderMode(HttpContext httpContext, ICompone
8888

8989
private static bool ModeEnablesPrerendering(IComponentRenderMode? mode) => mode switch
9090
{
91-
ServerRenderMode { Prerender: true } => true,
92-
WebAssemblyRenderMode { Prerender: true } => true,
93-
AutoRenderMode { Prerender: true } => true,
91+
InteractiveServerRenderMode { Prerender: true } => true,
92+
InteractiveWebAssemblyRenderMode { Prerender: true } => true,
93+
InteractiveAutoRenderMode { Prerender: true } => true,
9494
_ => false
9595
};
9696

src/Components/Endpoints/src/Rendering/SSRRenderModeBoundary.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ public SSRRenderModeBoundary(
4343
_renderMode = renderMode;
4444
_prerender = renderMode switch
4545
{
46-
ServerRenderMode mode => mode.Prerender,
47-
WebAssemblyRenderMode mode => mode.Prerender,
48-
AutoRenderMode mode => mode.Prerender,
46+
InteractiveServerRenderMode mode => mode.Prerender,
47+
InteractiveWebAssemblyRenderMode mode => mode.Prerender,
48+
InteractiveAutoRenderMode mode => mode.Prerender,
4949
_ => throw new ArgumentException($"Server-side rendering does not support the render mode '{renderMode}'.", nameof(renderMode))
5050
};
5151
}
@@ -65,14 +65,14 @@ private static void AssertRenderModeIsConfigured(HttpContext httpContext, Type c
6565
var configuredModes = configuredRenderModesMetadata.ConfiguredRenderModes;
6666

6767
// We have to allow for specified rendermodes being subclases of the known types
68-
if (renderMode is ServerRenderMode || renderMode is AutoRenderMode)
68+
if (renderMode is InteractiveServerRenderMode || renderMode is InteractiveAutoRenderMode)
6969
{
70-
AssertRenderModeIsConfigured<ServerRenderMode>(componentType, renderMode, configuredModes, "AddServerRenderMode");
70+
AssertRenderModeIsConfigured<InteractiveServerRenderMode>(componentType, renderMode, configuredModes, "AddInteractiveServerRenderMode");
7171
}
7272

73-
if (renderMode is WebAssemblyRenderMode || renderMode is AutoRenderMode)
73+
if (renderMode is InteractiveWebAssemblyRenderMode || renderMode is InteractiveAutoRenderMode)
7474
{
75-
AssertRenderModeIsConfigured<WebAssemblyRenderMode>(componentType, renderMode, configuredModes, "AddWebAssemblyRenderMode");
75+
AssertRenderModeIsConfigured<InteractiveWebAssemblyRenderMode>(componentType, renderMode, configuredModes, "AddInteractiveWebAssemblyRenderMode");
7676
}
7777
}
7878

@@ -165,13 +165,13 @@ public ComponentMarker ToMarker(HttpContext httpContext, int sequence, object? k
165165

166166
var marker = _renderMode switch
167167
{
168-
ServerRenderMode server => ComponentMarker.Create(ComponentMarker.ServerMarkerType, server.Prerender, _markerKey),
169-
WebAssemblyRenderMode webAssembly => ComponentMarker.Create(ComponentMarker.WebAssemblyMarkerType, webAssembly.Prerender, _markerKey),
170-
AutoRenderMode auto => ComponentMarker.Create(ComponentMarker.AutoMarkerType, auto.Prerender, _markerKey),
168+
InteractiveServerRenderMode server => ComponentMarker.Create(ComponentMarker.ServerMarkerType, server.Prerender, _markerKey),
169+
InteractiveWebAssemblyRenderMode webAssembly => ComponentMarker.Create(ComponentMarker.WebAssemblyMarkerType, webAssembly.Prerender, _markerKey),
170+
InteractiveAutoRenderMode auto => ComponentMarker.Create(ComponentMarker.AutoMarkerType, auto.Prerender, _markerKey),
171171
_ => throw new UnreachableException($"Unknown render mode {_renderMode.GetType().FullName}"),
172172
};
173173

174-
if (_renderMode is ServerRenderMode or AutoRenderMode)
174+
if (_renderMode is InteractiveServerRenderMode or InteractiveAutoRenderMode)
175175
{
176176
// Lazy because we don't actually want to require a whole chain of services including Data Protection
177177
// to be required unless you actually use Server render mode.
@@ -181,7 +181,7 @@ public ComponentMarker ToMarker(HttpContext httpContext, int sequence, object? k
181181
serverComponentSerializer.SerializeInvocation(ref marker, invocationId, _componentType, parameters);
182182
}
183183

184-
if (_renderMode is WebAssemblyRenderMode or AutoRenderMode)
184+
if (_renderMode is InteractiveWebAssemblyRenderMode or InteractiveAutoRenderMode)
185185
{
186186
WebAssemblyComponentSerializer.SerializeInvocation(ref marker, _componentType, parameters);
187187
}

src/Components/Endpoints/test/EndpointHtmlRendererTest.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public async Task CanRender_ParameterlessComponent_ClientMode()
5353
var writer = new StringWriter();
5454

5555
// Act
56-
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), new WebAssemblyRenderMode(prerender: false), ParameterView.Empty);
56+
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), new InteractiveWebAssemblyRenderMode(prerender: false), ParameterView.Empty);
5757
await renderer.Dispatcher.InvokeAsync(() => result.WriteTo(writer, HtmlEncoder.Default));
5858
var content = writer.ToString();
5959
var match = Regex.Match(content, ComponentPattern);
@@ -76,7 +76,7 @@ public async Task CanPrerender_ParameterlessComponent_ClientMode()
7676
var writer = new StringWriter();
7777

7878
// Act
79-
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), RenderMode.WebAssembly, ParameterView.Empty);
79+
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), RenderMode.InteractiveWebAssembly, ParameterView.Empty);
8080
await renderer.Dispatcher.InvokeAsync(() => result.WriteTo(writer, HtmlEncoder.Default));
8181
var content = writer.ToString();
8282
var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline);
@@ -115,7 +115,7 @@ public async Task CanRender_ComponentWithParameters_ClientMode()
115115

116116
// Act
117117
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent),
118-
new WebAssemblyRenderMode(prerender: false),
118+
new InteractiveWebAssemblyRenderMode(prerender: false),
119119
ParameterView.FromDictionary(new Dictionary<string, object>
120120
{
121121
{ "Name", "Daniel" }
@@ -152,7 +152,7 @@ public async Task CanRender_ComponentWithNullParameters_ClientMode()
152152

153153
// Act
154154
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent),
155-
new WebAssemblyRenderMode(prerender: false),
155+
new InteractiveWebAssemblyRenderMode(prerender: false),
156156
ParameterView.FromDictionary(new Dictionary<string, object>
157157
{
158158
{ "Name", null }
@@ -187,7 +187,7 @@ public async Task CanPrerender_ComponentWithParameters_ClientMode()
187187

188188
// Act
189189
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent),
190-
RenderMode.WebAssembly,
190+
RenderMode.InteractiveWebAssembly,
191191
ParameterView.FromDictionary(new Dictionary<string, object>
192192
{
193193
{ "Name", "Daniel" }
@@ -236,7 +236,7 @@ public async Task CanPrerender_ComponentWithNullParameters_ClientMode()
236236

237237
// Act
238238
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent),
239-
RenderMode.WebAssembly,
239+
RenderMode.InteractiveWebAssembly,
240240
ParameterView.FromDictionary(new Dictionary<string, object>
241241
{
242242
{ "Name", null }
@@ -300,7 +300,7 @@ public async Task CanRender_ParameterlessComponent_ServerMode()
300300
.ToTimeLimitedDataProtector();
301301

302302
// Act
303-
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), new ServerRenderMode(false), ParameterView.Empty);
303+
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), new InteractiveServerRenderMode(false), ParameterView.Empty);
304304
var content = await renderer.Dispatcher.InvokeAsync(() => HtmlContentToString(result));
305305
var match = Regex.Match(content, ComponentPattern);
306306

@@ -332,7 +332,7 @@ public async Task CanPrerender_ParameterlessComponent_ServerMode()
332332
.ToTimeLimitedDataProtector();
333333

334334
// Act
335-
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), RenderMode.Server, ParameterView.Empty);
335+
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), RenderMode.InteractiveServer, ParameterView.Empty);
336336
var content = await renderer.Dispatcher.InvokeAsync(() => HtmlContentToString(result));
337337
var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline);
338338

@@ -376,8 +376,8 @@ public async Task Prerender_ServerAndClientComponentUpdatesInvokedPrerenderModes
376376

377377
// Act
378378
var parameters = ParameterView.FromDictionary(new Dictionary<string, object> { { "Name", "SomeName" } });
379-
var server = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), RenderMode.Server, parameters);
380-
var client = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), RenderMode.WebAssembly, parameters);
379+
var server = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), RenderMode.InteractiveServer, parameters);
380+
var client = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), RenderMode.InteractiveWebAssembly, parameters);
381381

382382
// Assert
383383
var (_, mode) = Assert.Single(httpContext.Items, (kvp) => kvp.Value is InvokedRenderModes);
@@ -393,11 +393,11 @@ public async Task CanRenderMultipleServerComponents()
393393
.ToTimeLimitedDataProtector();
394394

395395
// Act
396-
var firstResult = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), new ServerRenderMode(true), ParameterView.Empty);
396+
var firstResult = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), new InteractiveServerRenderMode(true), ParameterView.Empty);
397397
var firstComponent = await renderer.Dispatcher.InvokeAsync(() => HtmlContentToString(firstResult));
398398
var firstMatch = Regex.Match(firstComponent, PrerenderedComponentPattern, RegexOptions.Multiline);
399399

400-
var secondResult = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), new ServerRenderMode(false), ParameterView.Empty);
400+
var secondResult = await renderer.PrerenderComponentAsync(httpContext, typeof(SimpleComponent), new InteractiveServerRenderMode(false), ParameterView.Empty);
401401
var secondComponent = await renderer.Dispatcher.InvokeAsync(() => HtmlContentToString(secondResult));
402402
var secondMatch = Regex.Match(secondComponent, ComponentPattern);
403403

@@ -451,7 +451,7 @@ public async Task CanRender_ComponentWithParameters_ServerMode()
451451

452452
// Act
453453
var parameters = ParameterView.FromDictionary(new Dictionary<string, object> { { "Name", "SomeName" } });
454-
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), new ServerRenderMode(false), parameters);
454+
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), new InteractiveServerRenderMode(false), parameters);
455455
var content = await renderer.Dispatcher.InvokeAsync(() => HtmlContentToString(result));
456456
var match = Regex.Match(content, ComponentPattern);
457457

@@ -490,7 +490,7 @@ public async Task CanRender_ComponentWithNullParameters_ServerMode()
490490

491491
// Act
492492
var parameters = ParameterView.FromDictionary(new Dictionary<string, object> { { "Name", null } });
493-
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), new ServerRenderMode(false), parameters);
493+
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), new InteractiveServerRenderMode(false), parameters);
494494
var content = await renderer.Dispatcher.InvokeAsync(() => HtmlContentToString(result));
495495
var match = Regex.Match(content, ComponentPattern);
496496

@@ -529,7 +529,7 @@ public async Task CanPrerender_ComponentWithParameters_ServerPrerenderedMode()
529529

530530
// Act
531531
var parameters = ParameterView.FromDictionary(new Dictionary<string, object> { { "Name", "SomeName" } });
532-
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), RenderMode.Server, parameters);
532+
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), RenderMode.InteractiveServer, parameters);
533533
var content = await renderer.Dispatcher.InvokeAsync(() => HtmlContentToString(result));
534534
var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline);
535535

@@ -580,7 +580,7 @@ public async Task CanPrerender_ComponentWithNullParameters_ServerPrerenderedMode
580580

581581
// Act
582582
var parameters = ParameterView.FromDictionary(new Dictionary<string, object> { { "Name", null } });
583-
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), RenderMode.Server, parameters);
583+
var result = await renderer.PrerenderComponentAsync(httpContext, typeof(GreetingComponent), RenderMode.InteractiveServer, parameters);
584584
var content = await renderer.Dispatcher.InvokeAsync(() => HtmlContentToString(result));
585585
var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline);
586586

src/Components/Endpoints/test/HotReloadServiceTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,15 +187,15 @@ private static RazorComponentEndpointDataSource<TComponent> CreateDataSource<TCo
187187
}
188188
else
189189
{
190-
result.Options.ConfiguredRenderModes.Add(new ServerRenderMode());
190+
result.Options.ConfiguredRenderModes.Add(new InteractiveServerRenderMode());
191191
}
192192

193193
return result;
194194
}
195195

196196
private class StaticComponent : ComponentBase { }
197197

198-
[RenderModeServer]
198+
[RenderModeInteractiveServer]
199199
private class ServerComponent : ComponentBase { }
200200

201201
private class MockEndpointProvider : RenderModeEndpointProvider

0 commit comments

Comments
 (0)