Fix: Apply Blazor WebAssembly JS Initializer configurations (loadBootResource, environment) before runtime initialization#65696
Conversation
…E2E tests for JavaScript initializers.
|
@dotnet-policy-service agree |
…ub.com/muharremtekin/aspnetcore into fix/issue-54358-js-initializers-timing
…d classic callbacks.
Cast fetchAndInvokeInitializers mock to 'any' to prevent strict TypeScript type checking on mock return values after import paths were updated to use relative paths (../src/...) instead of module aliases.
|
Thanks for your contribution. I remember looking into this in the past. I believe there is a smaller/simpler fix. If I recall correctly the issue was were trying to initialize the runtime before calling the initializers (beware I took a really quick look, maybe your fix is the same). |
|
cc @maraf |
|
@javiercn Yes I think it's the same thing, I just added lots of tests to ensure it fixed correctly. |
| onConfigLoadedCallback?.(loadedConfig); | ||
|
|
||
| jsInitializer = await fetchAndInvokeInitializers(options, loadedConfig); | ||
|
|
There was a problem hiding this comment.
@maraf why we are doing fetchAndInvokeInitializers here and not only in runtime ?
There was a problem hiding this comment.
Initializers is a blazor feature, not a wasm feature
There was a problem hiding this comment.
There was a problem hiding this comment.
I know wasm has it too, it got added there too when we did a refactor a couple years later. The blazor feature predates the wasm feature #34798
There was a problem hiding this comment.
yeah, why we didn't finish the refactoring and unify it ?
There was a problem hiding this comment.
Because we can't. This is used by libraries and we shouldn't break them, and I believe there were some things that could be done in the Blazor version that couldn't be done in the wasm version.
To be very clear, we can't just remove this feature for blazor. it's used on server, ssr, webview and (wasm) where wasm also added its own hooks. And it's the pattern libraries have to bring their JS. This should keep working for backwards compatibility.
|
Looks like this PR hasn't been active for some time and the codebase could have been changed in the meantime. |
Fixes #54358
Description
This PR fixes a bug where configurations set inside beforeStart JS Initializer callbacks (such as loadBootResource,
environment, and configureRuntime) were ignored in Blazor WebAssembly because the .NET runtime was being initialized before these callbacks had a chance to apply their modifications.Root Cause
In MonoPlatform.ts, the calls to
dotnet.withResourceLoader(),dotnet.withApplicationEnvironment(), etc., were being made synchronously inside createRuntimeInstance(), capturing theoptionsobject before the onConfigLoaded callback was fired. Since JS Initializer callbacks are invoked viafetchAndInvokeInitializersinside onConfigLoaded, any modifications they made tooptionswere lost.The Fix
Moved the
dotnet.with*()configuration calls from createRuntimeInstance() into the onConfigLoaded callback, ensuring they are executed afterfetchAndInvokeInitializershas run and applied user configurations.Is this safe? Yes. According to the
dotnet/runtimesource code (src/mono/browser/runtime/loader/config.ts), onConfigLoaded is explicitlyawaited by the runtime exactly for this scenario:This guarantees that WebAssembly resource downloads and initialization will wait for these configurations to be applied.
Testing
To ensure this behavior is correct and doesn't regress:
Unit Tests (Jest)
@internal.fetchAndInvokeInitializersmust fire before withResourceLoader.fetchAndInvokeInitializersmust fire before withApplicationEnvironment.Blazor.start(options)properties are still respected.E2E Integration Tests (Selenium)
Risk Assessment
Low. This only delays the reading of
optionsproperties until onConfigLoaded, which is the correct lifecycle hook intended by the runtime. All 40Web.JStest suites and E2E tests pass.