From 1eb8095581394d0436cde2bbec578e4b1df057f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Fri, 13 Jun 2025 13:20:27 +0200 Subject: [PATCH 1/4] Support for new schema of initializers in boot config --- .../JSInitializers.WebAssembly.ts | 15 +++++++++++++-- .../Web.JS/src/JSInitializers/JSInitializers.ts | 17 +++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts index 4c6e202f01a2..6cd8b120893d 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts @@ -4,7 +4,7 @@ import { MonoConfig } from '@microsoft/dotnet-runtime'; import { WebAssemblyStartOptions } from '../Platform/WebAssemblyStartOptions'; import { WebRendererId } from '../Rendering/WebRendererId'; -import { JSInitializer } from './JSInitializers'; +import { JSAsset, JSInitializer } from './JSInitializers'; export async function fetchAndInvokeInitializers(options: Partial, loadedConfig: MonoConfig): Promise { if (options.initializers) { @@ -20,7 +20,18 @@ export async function fetchAndInvokeInitializers(options: Partial ({ + name, + })) as JSAsset[]; + } + await jsInitializer.importInitializersAsync(initializers, initializerArguments); return jsInitializer; } diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts index 5ab4bc7950be..c65f114b9527 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts @@ -24,6 +24,11 @@ export type BlazorInitializer = { afterServerStarted: AfterBlazorServerStartedCallback, }; +export type JSAsset = { + moduleExports?: any | Promise, + name?: string; // actually URL +} + export class JSInitializer { private afterStartedCallbacks: AfterBlazorStartedCallback[] = []; @@ -38,7 +43,7 @@ export class JSInitializer { } } - async importInitializersAsync(initializerFiles: string[], initializerArguments: unknown[]): Promise { + async importInitializersAsync(initializerFiles: JSAsset[], initializerArguments: unknown[]): Promise { // This code is not called on WASM, because library intializers are imported by runtime. await Promise.all(initializerFiles.map(f => importAndInvokeInitializer(this, f))); @@ -50,9 +55,13 @@ export class JSInitializer { return path; } - async function importAndInvokeInitializer(jsInitializer: JSInitializer, path: string): Promise { - const adjustedPath = adjustPath(path); - const initializer = await import(/* webpackIgnore: true */ adjustedPath) as Partial; + async function importAndInvokeInitializer(jsInitializer: JSInitializer, asset: JSAsset): Promise { + if (!asset.moduleExports) { + const adjustedPath = adjustPath(asset.name!); + asset.moduleExports = await import(/* webpackIgnore: true */ adjustedPath); + } + + const initializer = asset.moduleExports as Partial; if (initializer === undefined) { return; } From 1c386dea546b1152f5855e7ee8aa0736ab152b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Fri, 13 Jun 2025 13:44:13 +0200 Subject: [PATCH 2/4] wip --- .../Web.JS/src/JSInitializers/JSInitializers.Server.ts | 6 ++++-- .../Web.JS/src/JSInitializers/JSInitializers.Web.ts | 6 ++++-- .../Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts | 4 ++-- .../Web.JS/src/JSInitializers/JSInitializers.WebView.ts | 6 ++++-- src/Components/Web.JS/src/JSInitializers/JSInitializers.ts | 3 ++- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.Server.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.Server.ts index 57f8ffdf6494..2faa949097be 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.Server.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.Server.ts @@ -3,7 +3,7 @@ import { CircuitStartOptions } from '../Platform/Circuits/CircuitStartOptions'; import { WebRendererId } from '../Rendering/WebRendererId'; -import { JSInitializer } from './JSInitializers'; +import { JSAsset, JSInitializer } from './JSInitializers'; export async function fetchAndInvokeInitializers(options: Partial) : Promise { if (options.initializers) { @@ -19,7 +19,9 @@ export async function fetchAndInvokeInitializers(options: Partial ({ + name, + })) as JSAsset[]; const jsInitializer = new JSInitializer(/* singleRuntime: */ true, undefined, undefined, WebRendererId.Server); await jsInitializer.importInitializersAsync(initializers, [options]); return jsInitializer; diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.Web.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.Web.ts index b72e012ac98d..c997676f35c2 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.Web.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.Web.ts @@ -4,14 +4,16 @@ import { Logger } from '../Platform/Logging/Logger'; import { WebStartOptions } from '../Platform/WebStartOptions'; import { discoverWebInitializers } from '../Services/ComponentDescriptorDiscovery'; -import { JSInitializer } from './JSInitializers'; +import { JSAsset, JSInitializer } from './JSInitializers'; export async function fetchAndInvokeInitializers(options: Partial, logger: Logger) : Promise { const initializersElement = discoverWebInitializers(document); if (!initializersElement) { return new JSInitializer(false, logger); } - const initializers: string[] = JSON.parse(atob(initializersElement)) as string[] ?? []; + const initializers = (JSON.parse(atob(initializersElement)) as string[] ?? []).map(name => ({ + name, + })) as JSAsset[]; const jsInitializer = new JSInitializer(false, logger); await jsInitializer.importInitializersAsync(initializers, [options]); return jsInitializer; diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts index 6cd8b120893d..81d9570bcb2e 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts @@ -22,9 +22,9 @@ export async function fetchAndInvokeInitializers(options: Partial ({ diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebView.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebView.ts index 0b46064ef5ea..4344474ae848 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebView.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebView.ts @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { JSInitializer } from './JSInitializers'; +import { JSAsset, JSInitializer } from './JSInitializers'; export async function fetchAndInvokeInitializers() : Promise { const jsInitializersResponse = await fetch('_framework/blazor.modules.json', { @@ -10,7 +10,9 @@ export async function fetchAndInvokeInitializers() : Promise { cache: 'no-cache', }); - const initializers: string[] = await jsInitializersResponse.json(); + const initializers = (await jsInitializersResponse.json()).map(name => ({ + name, + })) as JSAsset[]; const jsInitializer = new JSInitializer(); await jsInitializer.importInitializersAsync(initializers, []); return jsInitializer; diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts index c65f114b9527..23902e957350 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.ts @@ -56,8 +56,9 @@ export class JSInitializer { } async function importAndInvokeInitializer(jsInitializer: JSInitializer, asset: JSAsset): Promise { + let adjustedPath; if (!asset.moduleExports) { - const adjustedPath = adjustPath(asset.name!); + adjustedPath = adjustPath(asset.name!); asset.moduleExports = await import(/* webpackIgnore: true */ adjustedPath); } From df292e7c97bee033ac129bbee204412dcc2475cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 19 Jun 2025 17:11:11 +0200 Subject: [PATCH 3/4] Import dotnet.js as relative dynamic import with literal --- src/Components/Web.JS/rollup.config.mjs | 10 ++++++++++ .../Web.JS/src/Platform/Mono/MonoPlatform.ts | 10 +++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Components/Web.JS/rollup.config.mjs b/src/Components/Web.JS/rollup.config.mjs index b30f1a738346..91fb668b0434 100644 --- a/src/Components/Web.JS/rollup.config.mjs +++ b/src/Components/Web.JS/rollup.config.mjs @@ -14,6 +14,16 @@ export default createBaseConfig({ }, dir: __dirname, updateConfig: (config, environment, _, input) => { + config.plugins.push({ + name: 'Resolve dotnet.js dynamic import', + resolveDynamicImport(source, importer) { + if (source === './dotnet.js') { + return { id: './dotnet.js', moduleSideEffects: false, external: 'relative' }; + } + return null; + } + }); + if (input.includes("WebView")) { config.output.sourcemap = 'inline'; } else { diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 9eff6375548c..bc3ca20a035b 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -116,22 +116,22 @@ async function importDotnetJs(startOptions: Partial): P throw new Error('This browser does not support WebAssembly.'); } - let src = '_framework/dotnet.js'; // Allow overriding the URI from which the dotnet.*.js file is loaded if (startOptions.loadBootResource) { const resourceType: WebAssemblyBootResourceType = 'dotnetjs'; - const customSrc = startOptions.loadBootResource(resourceType, 'dotnet.js', src, '', 'js-module-dotnet'); + const customSrc = startOptions.loadBootResource(resourceType, 'dotnet.js', '_framework/dotnet.js', '', 'js-module-dotnet'); if (typeof (customSrc) === 'string') { - src = customSrc; + const absoluteSrc = (new URL(customSrc, document.baseURI)).toString(); + return await import(/* webpackIgnore: true */ absoluteSrc); } else if (customSrc) { // Since we must load this via a import, it's only valid to supply a URI (and not a Request, say) throw new Error(`For a ${resourceType} resource, custom loaders must supply a URI string.`); } } - const absoluteSrc = (new URL(src, document.baseURI)).toString(); - return await import(/* webpackIgnore: true */ absoluteSrc); + // @ts-ignore: This dynamic import is handled at runtime and does not need a type declaration. + return await import(/* webpackIgnore: true */ "./dotnet.js"); } function prepareRuntimeConfig(options: Partial, onConfigLoadedCallback?: (loadedConfig: MonoConfig) => void): DotnetModuleConfig { From fc3209531f3f368015ede76f1b1800942fac018b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Fi=C5=A1era?= Date: Thu, 19 Jun 2025 18:27:34 +0200 Subject: [PATCH 4/4] fix --- .../src/JSInitializers/JSInitializers.WebAssembly.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts index 81d9570bcb2e..5860073e2c8b 100644 --- a/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts +++ b/src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts @@ -21,13 +21,17 @@ export async function fetchAndInvokeInitializers(options: Partial ({ + initializers = Object.keys(configInitializers).map(name => ({ name, })) as JSAsset[]; }