diff --git a/src/Components/Browser.JS/src/Boot.Server.ts b/src/Components/Browser.JS/src/Boot.Server.ts index 74c2c295a3..04c3e0305d 100644 --- a/src/Components/Browser.JS/src/Boot.Server.ts +++ b/src/Components/Browser.JS/src/Boot.Server.ts @@ -2,7 +2,7 @@ import '@dotnet/jsinterop'; import './GlobalExports'; import * as signalR from '@aspnet/signalr'; import { MessagePackHubProtocol } from '@aspnet/signalr-protocol-msgpack'; -import { fetchBootConfigAsync, loadEmbeddedResourcesAsync } from './BootCommon'; +import { fetchBootConfigAsync, loadEmbeddedResourcesAsync, shouldAutoStart } from './BootCommon'; import { CircuitHandler } from './Platform/Circuits/CircuitHandler'; import { AutoReconnectCircuitHandler } from './Platform/Circuits/AutoReconnectCircuitHandler'; import RenderQueue from './Platform/Circuits/RenderQueue'; @@ -10,9 +10,21 @@ import { ConsoleLogger } from './Platform/Logging/Loggers'; import { LogLevel, ILogger } from './Platform/Logging/ILogger'; import { discoverPrerenderedCircuits, startCircuit } from './Platform/Circuits/CircuitManager'; -let renderingFailed = false; -async function boot(): Promise { +type SignalRBuilder = (builder: signalR.HubConnectionBuilder) => void; +interface BlazorOptions { + configureSignalR?: SignalRBuilder, +}; + +let renderingFailed = false; +let started = false; + +async function boot(options?: BlazorOptions): Promise { + + if (started) { + throw new Error('Blazor has already started.'); + } + started = true; // For development. // Simply put a break point here and modify the log level during @@ -30,7 +42,9 @@ async function boot(): Promise { return loadEmbeddedResourcesAsync(bootConfig); }); - const initialConnection = await initializeConnection(circuitHandlers, logger); + // pass options.configureSignalR to configure the signalR.HubConnectionBuilder + const configureSignalR = (options && options.configureSignalR) || null; + const initialConnection = await initializeConnection(configureSignalR, circuitHandlers, logger); const circuits = discoverPrerenderedCircuits(document); for (let i = 0; i < circuits.length; i++) { @@ -55,7 +69,7 @@ async function boot(): Promise { // We can't reconnect after a failure, so exit early. return false; } - const reconnection = await initializeConnection(circuitHandlers, logger); + const reconnection = await initializeConnection(configureSignalR, circuitHandlers, logger); const results = await Promise.all(circuits.map(circuit => circuit.reconnect(reconnection))); if (reconnectionFailed(results)) { @@ -81,14 +95,20 @@ async function boot(): Promise { } } -async function initializeConnection(circuitHandlers: CircuitHandler[], logger: ILogger): Promise { +async function initializeConnection(configureSignalR: SignalRBuilder | null, circuitHandlers: CircuitHandler[], logger: ILogger): Promise { + const hubProtocol = new MessagePackHubProtocol(); (hubProtocol as any).name = 'blazorpack'; - const connection = new signalR.HubConnectionBuilder() + + const connectionBuilder = new signalR.HubConnectionBuilder() .withUrl('_blazor') - .withHubProtocol(hubProtocol) - .configureLogging(signalR.LogLevel.Information) - .build(); + .withHubProtocol(hubProtocol); + + if (configureSignalR) { + configureSignalR(connectionBuilder); + } + + const connection = connectionBuilder.build(); connection.on('JS.BeginInvokeJS', DotNet.jsCallDispatcher.beginInvokeJSFromDotNet); connection.on('JS.RenderBatch', (browserRendererId: number, batchId: number, batchData: Uint8Array) => { @@ -131,4 +151,7 @@ function unhandledError(connection: signalR.HubConnection, err: Error, logger: I } } -boot(); +window['Blazor'].start = boot; +if (shouldAutoStart()) { + boot(); +} diff --git a/src/Components/Browser.JS/src/Boot.WebAssembly.ts b/src/Components/Browser.JS/src/Boot.WebAssembly.ts index 86eac84de1..fe17439bfd 100644 --- a/src/Components/Browser.JS/src/Boot.WebAssembly.ts +++ b/src/Components/Browser.JS/src/Boot.WebAssembly.ts @@ -6,9 +6,17 @@ import { getAssemblyNameFromUrl } from './Platform/Url'; import { renderBatch } from './Rendering/Renderer'; import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch'; import { Pointer } from './Platform/Platform'; -import { fetchBootConfigAsync, loadEmbeddedResourcesAsync } from './BootCommon'; +import { fetchBootConfigAsync, loadEmbeddedResourcesAsync, shouldAutoStart } from './BootCommon'; + +let started = false; + +async function boot(options?: any) { + + if (started) { + throw new Error('Blazor has already started.'); + } + started = true; -async function boot() { // Configure environment for execution under Mono WebAssembly with shared-memory rendering const platform = Environment.setPlatform(monoPlatform); window['Blazor'].platform = platform; @@ -43,4 +51,7 @@ async function boot() { platform.callEntryPoint(mainAssemblyName, bootConfig.entryPoint, []); } -boot(); +window['Blazor'].start = boot; +if (shouldAutoStart()) { + boot(); +} \ No newline at end of file diff --git a/src/Components/Browser.JS/src/BootCommon.ts b/src/Components/Browser.JS/src/BootCommon.ts index 1506f3b86b..959cac3e16 100644 --- a/src/Components/Browser.JS/src/BootCommon.ts +++ b/src/Components/Browser.JS/src/BootCommon.ts @@ -37,3 +37,10 @@ interface BootJsonData { jsReferences: string[]; linkerEnabled: boolean; } + +// Tells you if the script was added without +export function shouldAutoStart() { + return document && + document.currentScript && + document.currentScript.getAttribute('autostart') !== 'false'; +} \ No newline at end of file diff --git a/src/Components/Browser.JS/src/GlobalExports.ts b/src/Components/Browser.JS/src/GlobalExports.ts index ecac3dc1dd..74c5228116 100644 --- a/src/Components/Browser.JS/src/GlobalExports.ts +++ b/src/Components/Browser.JS/src/GlobalExports.ts @@ -1,8 +1,6 @@ -import { platform } from './Environment'; import { navigateTo, internalFunctions as uriHelperInternalFunctions } from './Services/UriHelper'; import { internalFunctions as httpInternalFunctions } from './Services/Http'; import { attachRootComponentToElement } from './Rendering/Renderer'; -import { Pointer } from './Platform/Platform'; // Make the following APIs available in global scope for invocation from JS window['Blazor'] = { @@ -13,4 +11,4 @@ window['Blazor'] = { http: httpInternalFunctions, uriHelper: uriHelperInternalFunctions } -}; +}; \ No newline at end of file diff --git a/src/Components/Components.sln b/src/Components/Components.sln index 82940049d6..956d991aff 100644 --- a/src/Components/Components.sln +++ b/src/Components/Components.sln @@ -210,6 +210,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\..\.editorconfig = ..\..\.editorconfig EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Components.Prerendering", "..\Mvc\Mvc.Components.Prerendering\src\Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj", "{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1324,6 +1326,18 @@ Global {9088E4E4-B855-457F-AE9E-D86709A5E1F4}.Release|x64.Build.0 = Debug|Any CPU {9088E4E4-B855-457F-AE9E-D86709A5E1F4}.Release|x86.ActiveCfg = Debug|Any CPU {9088E4E4-B855-457F-AE9E-D86709A5E1F4}.Release|x86.Build.0 = Debug|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x64.ActiveCfg = Debug|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x64.Build.0 = Debug|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x86.ActiveCfg = Debug|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x86.Build.0 = Debug|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|Any CPU.Build.0 = Release|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x64.ActiveCfg = Release|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x64.Build.0 = Release|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x86.ActiveCfg = Release|Any CPU + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1423,6 +1437,7 @@ Global {04262990-929C-42BF-85A9-21C25FA95617} = {2FC10057-7A0A-4E34-8302-879925BC0102} {DC47C40A-FC38-44E4-94A4-ADE794E76309} = {2FC10057-7A0A-4E34-8302-879925BC0102} {9088E4E4-B855-457F-AE9E-D86709A5E1F4} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF} + {3A4132B6-60DA-43A0-8E7B-4BF346F3247C} = {2FC10057-7A0A-4E34-8302-879925BC0102} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CC3C47E1-AD1A-4619-9CD3-E08A0148E5CE} diff --git a/src/Components/test/testassets/BasicTestApp/wwwroot/index.html b/src/Components/test/testassets/BasicTestApp/wwwroot/index.html index 57b0015a9f..eb90752c41 100644 --- a/src/Components/test/testassets/BasicTestApp/wwwroot/index.html +++ b/src/Components/test/testassets/BasicTestApp/wwwroot/index.html @@ -3,7 +3,7 @@ Basic test app - + diff --git a/src/Components/test/testassets/ComponentsApp.Server/Pages/Index.cshtml b/src/Components/test/testassets/ComponentsApp.Server/Pages/Index.cshtml index ff0648e9db..9c4a241fb1 100644 --- a/src/Components/test/testassets/ComponentsApp.Server/Pages/Index.cshtml +++ b/src/Components/test/testassets/ComponentsApp.Server/Pages/Index.cshtml @@ -15,7 +15,13 @@ @(await Html.RenderComponentAsync(new { Name="Guest" })) - - + + diff --git a/src/Components/test/testassets/TestServer/Pages/PrerenderedHost.cshtml b/src/Components/test/testassets/TestServer/Pages/PrerenderedHost.cshtml index dd5ee32cbc..ba3bf3812a 100644 --- a/src/Components/test/testassets/TestServer/Pages/PrerenderedHost.cshtml +++ b/src/Components/test/testassets/TestServer/Pages/PrerenderedHost.cshtml @@ -15,15 +15,11 @@ interactive states, we only load the .js file when told to. *@
- - +