diff --git a/src/libraries/sendtohelix-browser.targets b/src/libraries/sendtohelix-browser.targets
index c1e5d9d5d20c4f..eba7ec0e95f2c7 100644
--- a/src/libraries/sendtohelix-browser.targets
+++ b/src/libraries/sendtohelix-browser.targets
@@ -138,6 +138,7 @@
<_XUnitTraitArg Condition="'$(TestUsingWorkloads)' == 'true'">-notrait category=no-workload
<_XUnitTraitArg Condition="'$(TestUsingWorkloads)' != 'true'">-trait category=no-workload
<_XUnitTraitArg Condition="'$(WasmFingerprintAssets)' == 'false'">$(_XUnitTraitArg) -trait category=no-fingerprinting
+ <_XUnitTraitArg Condition="'$(WasmBundlerFriendlyBootConfig)' == 'true'">$(_XUnitTraitArg) -trait category=bundler-friendly
diff --git a/src/libraries/sendtohelix-wasm.targets b/src/libraries/sendtohelix-wasm.targets
index f92b17d4fc9723..3a8b618f54f3d9 100644
--- a/src/libraries/sendtohelix-wasm.targets
+++ b/src/libraries/sendtohelix-wasm.targets
@@ -14,6 +14,7 @@
NoWorkload-
NoWebcil-
NoFingerprint-
+ JavascriptBundler-
$(WorkItemPrefix)ST-
$(WorkItemPrefix)MT-
@@ -50,7 +51,7 @@
-
+
$(_BuildWasmAppsPayloadArchive)
set "HELIX_XUNIT_ARGS=-class %(Identity)"
export "HELIX_XUNIT_ARGS=-class %(Identity)"
@@ -58,7 +59,7 @@
$(_workItemTimeout)
-
+
$(_BuildWasmAppsPayloadArchive)
$(HelixCommand)
$(_workItemTimeout)
diff --git a/src/libraries/sendtohelix.proj b/src/libraries/sendtohelix.proj
index a916ea3e836cc3..c121c3c009d51b 100644
--- a/src/libraries/sendtohelix.proj
+++ b/src/libraries/sendtohelix.proj
@@ -88,10 +88,12 @@
<_TestUsingWorkloadsValues Include="true;false" />
<_TestUsingWebcilValues Include="true;false" Condition="'$(TargetOS)' == 'browser'" />
<_TestUsingFingerprintingValues Include="true;false" Condition="'$(TargetOS)' == 'browser'" />
+ <_TestUsingJavascriptBundlerValues Include="true;false" Condition="'$(TargetOS)' == 'browser'" />
<_TestUsingCrossProductValuesTemp Include="@(_TestUsingWorkloadsValues)">
%(_TestUsingWorkloadsValues.Identity)
+ false
<_TestUsingCrossProductValuesTemp2 Include="@(_TestUsingCrossProductValuesTemp)">
%(_TestUsingWebcilValues.Identity)
@@ -102,9 +104,24 @@
<_TestUsingCrossProductValues Remove="@(_TestUsingCrossProductValues)" Condition="'%(_TestUsingCrossProductValues.Workloads)' == 'false' and '%(_TestUsingCrossProductValues.Fingerprinting)' == 'false'" />
+
+
+ <_TestUsingCrossProductValues Include="JavaScriptBundlerFriendly">
+ true
+ true
+ true
+ true
+
<_BuildWasmAppsProjectsToBuild Include="$(PerScenarioProjectFile)">
- $(_PropertiesToPass);Scenario=BuildWasmApps;TestArchiveRuntimeFile=$(TestArchiveRuntimeFile);TestUsingWorkloads=%(_TestUsingCrossProductValues.Workloads);WasmEnableWebcil=%(_TestUsingCrossProductValues.Webcil);WasmFingerprintAssets=%(_TestUsingCrossProductValues.Fingerprinting)
+
+ $(_PropertiesToPass);Scenario=BuildWasmApps;
+ TestArchiveRuntimeFile=$(TestArchiveRuntimeFile);
+ TestUsingWorkloads=%(_TestUsingCrossProductValues.Workloads);
+ WasmEnableWebcil=%(_TestUsingCrossProductValues.Webcil);
+ WasmFingerprintAssets=%(_TestUsingCrossProductValues.Fingerprinting);
+ WasmBundlerFriendlyBootConfig=%(_TestUsingCrossProductValues.BundlerFriendly)
+
%(_BuildWasmAppsProjectsToBuild.AdditionalProperties);NeedsToBuildWasmAppsOnHelix=$(NeedsToBuildWasmAppsOnHelix)
diff --git a/src/libraries/sendtohelixhelp.proj b/src/libraries/sendtohelixhelp.proj
index b01079ab6f07d1..87d3c1d371f808 100644
--- a/src/libraries/sendtohelixhelp.proj
+++ b/src/libraries/sendtohelixhelp.proj
@@ -159,6 +159,7 @@
+
@@ -348,7 +349,7 @@
+ Text="Scenario: $(Scenario), TestUsingWorkloads: $(TestUsingWorkloads), WasmEnableWebcil: $(WasmEnableWebcil), WasmFingerprintAssets: $(WasmFingerprintAssets), WasmBundlerFriendlyBootConfig: $(WasmBundlerFriendlyBootConfig)" />
diff --git a/src/mono/browser/runtime/dotnet.d.ts b/src/mono/browser/runtime/dotnet.d.ts
index 5700961fd53581..7184ad364453d6 100644
--- a/src/mono/browser/runtime/dotnet.d.ts
+++ b/src/mono/browser/runtime/dotnet.d.ts
@@ -220,10 +220,7 @@ type MonoConfig = {
* Gets the application culture. This is a name specified in the BCP 47 format. See https://tools.ietf.org/html/bcp47
*/
applicationCulture?: string;
- /**
- * definition of assets to load along with the runtime.
- */
- resources?: ResourceGroups;
+ resources?: Assets;
/**
* appsettings files to load to VFS
*/
@@ -247,36 +244,84 @@ type MonoConfig = {
type ResourceExtensions = {
[extensionName: string]: ResourceList;
};
-interface ResourceGroups {
+interface Assets {
hash?: string;
- fingerprinting?: {
- [name: string]: string;
- };
- coreAssembly?: ResourceList;
- assembly?: ResourceList;
- lazyAssembly?: ResourceList;
- corePdb?: ResourceList;
- pdb?: ResourceList;
- jsModuleWorker?: ResourceList;
- jsModuleDiagnostics?: ResourceList;
- jsModuleNative: ResourceList;
- jsModuleRuntime: ResourceList;
- wasmSymbols?: ResourceList;
- wasmNative: ResourceList;
- icu?: ResourceList;
+ coreAssembly?: AssemblyAsset[];
+ assembly?: AssemblyAsset[];
+ lazyAssembly?: AssemblyAsset[];
+ corePdb?: PdbAsset[];
+ pdb?: PdbAsset[];
+ jsModuleWorker?: JsAsset[];
+ jsModuleDiagnostics?: JsAsset[];
+ jsModuleNative: JsAsset[];
+ jsModuleRuntime: JsAsset[];
+ wasmSymbols?: SymbolsAsset[];
+ wasmNative: WasmAsset[];
+ icu?: IcuAsset[];
satelliteResources?: {
- [cultureName: string]: ResourceList;
+ [cultureName: string]: AssemblyAsset[];
};
- modulesAfterConfigLoaded?: ResourceList;
- modulesAfterRuntimeReady?: ResourceList;
+ modulesAfterConfigLoaded?: JsAsset[];
+ modulesAfterRuntimeReady?: JsAsset[];
extensions?: ResourceExtensions;
- coreVfs?: {
- [virtualPath: string]: ResourceList;
- };
- vfs?: {
- [virtualPath: string]: ResourceList;
- };
+ coreVfs?: VfsAsset[];
+ vfs?: VfsAsset[];
}
+type Asset = {
+ /**
+ * this should be absolute url to the asset
+ */
+ resolvedUrl?: string;
+ /**
+ * If true, the runtime startup would not fail if the asset download was not successful.
+ */
+ isOptional?: boolean;
+ /**
+ * If provided, runtime doesn't have to fetch the data.
+ * Runtime would set the buffer to null after instantiation to free the memory.
+ */
+ buffer?: ArrayBuffer | Promise;
+ /**
+ * It's metadata + fetch-like Promise
+ * If provided, the runtime doesn't have to initiate the download. It would just await the response.
+ */
+ pendingDownload?: LoadingResource;
+};
+type WasmAsset = Asset & {
+ name: string;
+ hash?: string | null | "";
+};
+type AssemblyAsset = Asset & {
+ virtualPath: string;
+ name: string;
+ hash?: string | null | "";
+};
+type PdbAsset = Asset & {
+ virtualPath: string;
+ name: string;
+ hash?: string | null | "";
+};
+type JsAsset = Asset & {
+ /**
+ * If provided, runtime doesn't have to import it's JavaScript modules.
+ * This will not work for multi-threaded runtime.
+ */
+ moduleExports?: any | Promise;
+ name?: string;
+};
+type SymbolsAsset = Asset & {
+ name: string;
+};
+type VfsAsset = Asset & {
+ virtualPath: string;
+ name: string;
+ hash?: string | null | "";
+};
+type IcuAsset = Asset & {
+ virtualPath: string;
+ name: string;
+ hash?: string | null | "";
+};
/**
* A "key" is name of the file, a "value" is optional hash for integrity check.
*/
diff --git a/src/mono/browser/runtime/lazyLoading.ts b/src/mono/browser/runtime/lazyLoading.ts
index 9787c18ac33ec3..2643a4d6b5cb79 100644
--- a/src/mono/browser/runtime/lazyLoading.ts
+++ b/src/mono/browser/runtime/lazyLoading.ts
@@ -3,7 +3,8 @@
import { loaderHelpers } from "./globals";
import { load_lazy_assembly } from "./managed-exports";
-import { AssetEntry } from "./types";
+import { type AssemblyAsset, type PdbAsset } from "./types";
+import { type AssetEntryInternal } from "./types/internal";
export async function loadLazyAssembly (assemblyNameToLoad: string): Promise {
const resources = loaderHelpers.config.resources!;
@@ -20,50 +21,35 @@ export async function loadLazyAssembly (assemblyNameToLoad: string): Promise {
- if (resources.fingerprinting && (asset.behavior == "assembly" || asset.behavior == "pdb" || asset.behavior == "resource")) {
- asset.virtualPath = getNonFingerprintedAssetName(asset.name);
- }
+ const addAsset = (asset: Asset, behavior: AssetBehaviors, isCore: boolean) => {
+ const assetEntry = asset as AssetEntryInternal;
+ assetEntry.behavior = behavior;
if (isCore) {
- asset.isCore = true;
- coreAssetsToLoad.push(asset);
+ assetEntry.isCore = true;
+ coreAssetsToLoad.push(assetEntry);
} else {
- assetsToLoad.push(asset);
+ assetsToLoad.push(assetEntry);
}
};
if (resources.coreAssembly) {
- for (const name in resources.coreAssembly) {
- addAsset({
- name,
- hash: resources.coreAssembly[name],
- behavior: "assembly"
- }, true);
+ for (let i = 0; i < resources.coreAssembly.length; i++) {
+ const asset = resources.coreAssembly[i];
+ addAsset(asset, "assembly", true);
}
}
if (resources.assembly) {
- for (const name in resources.assembly) {
- addAsset({
- name,
- hash: resources.assembly[name],
- behavior: "assembly"
- }, !resources.coreAssembly); // if there are no core assemblies, then all assemblies are core
+ for (let i = 0; i < resources.assembly.length; i++) {
+ const asset = resources.assembly[i];
+ addAsset(asset, "assembly", !resources.coreAssembly);
}
}
if (config.debugLevel != 0 && loaderHelpers.isDebuggingSupported()) {
if (resources.corePdb) {
- for (const name in resources.corePdb) {
- addAsset({
- name,
- hash: resources.corePdb[name],
- behavior: "pdb"
- }, true);
+ for (let i = 0; i < resources.corePdb.length; i++) {
+ const asset = resources.corePdb[i];
+ addAsset(asset, "pdb", true);
}
}
if (resources.pdb) {
- for (const name in resources.pdb) {
- addAsset({
- name,
- hash: resources.pdb[name],
- behavior: "pdb"
- }, !resources.corePdb); // if there are no core pdbs, then all pdbs are core
+ for (let i = 0; i < resources.pdb.length; i++) {
+ const asset = resources.pdb[i];
+ addAsset(asset, "pdb", !resources.corePdb);
}
}
}
if (config.loadAllSatelliteResources && resources.satelliteResources) {
for (const culture in resources.satelliteResources) {
- for (const name in resources.satelliteResources[culture]) {
- addAsset({
- name,
- hash: resources.satelliteResources[culture][name],
- behavior: "resource",
- culture
- }, !resources.coreAssembly);
+ for (let i = 0; i < resources.satelliteResources[culture].length; i++) {
+ const asset = resources.satelliteResources[culture][i] as AssemblyAsset & AssetEntryInternal;
+ asset.culture = culture;
+ addAsset(asset, "resource", !resources.coreAssembly);
}
}
}
if (resources.coreVfs) {
- for (const virtualPath in resources.coreVfs) {
- for (const name in resources.coreVfs[virtualPath]) {
- addAsset({
- name,
- hash: resources.coreVfs[virtualPath][name],
- behavior: "vfs",
- virtualPath
- }, true);
- }
+ for (let i = 0; i < resources.coreVfs.length; i++) {
+ const asset = resources.coreVfs[i];
+ addAsset(asset, "vfs", true);
}
}
if (resources.vfs) {
- for (const virtualPath in resources.vfs) {
- for (const name in resources.vfs[virtualPath]) {
- addAsset({
- name,
- hash: resources.vfs[virtualPath][name],
- behavior: "vfs",
- virtualPath
- }, !resources.coreVfs);
- }
+ for (let i = 0; i < resources.vfs.length; i++) {
+ const asset = resources.vfs[i];
+ addAsset(asset, "vfs", !resources.coreVfs);
}
}
const icuDataResourceName = getIcuResourceName(config);
if (icuDataResourceName && resources.icu) {
- for (const name in resources.icu) {
- if (name === icuDataResourceName) {
- assetsToLoad.push({
- name,
- hash: resources.icu[name],
- behavior: "icu",
- loadRemote: true
- });
+ for (let i = 0; i < resources.icu.length; i++) {
+ const asset = resources.icu[i];
+ if (asset.name === icuDataResourceName) {
+ addAsset(asset, "icu", false);
}
}
}
if (resources.wasmSymbols) {
- for (const name in resources.wasmSymbols) {
- coreAssetsToLoad.push({
- name,
- hash: resources.wasmSymbols[name],
- behavior: "symbols"
- });
+ for (let i = 0; i < resources.wasmSymbols.length; i++) {
+ const asset = resources.wasmSymbols[i];
+ addAsset(asset, "symbols", false);
}
}
}
@@ -450,15 +410,6 @@ export function prepareAssets () {
config.assets = [...coreAssetsToLoad, ...assetsToLoad, ...modulesAssets];
}
-export function getNonFingerprintedAssetName (assetName: string) {
- const fingerprinting = loaderHelpers.config.resources?.fingerprinting;
- if (fingerprinting && fingerprinting[assetName]) {
- return fingerprinting[assetName];
- }
-
- return assetName;
-}
-
export function prepareAssetsWorker () {
const config = loaderHelpers.config;
mono_assert(config.assets, "config.assets must be defined");
diff --git a/src/mono/browser/runtime/loader/config.ts b/src/mono/browser/runtime/loader/config.ts
index b0cb66ad8d47d8..f882b2b1e192ea 100644
--- a/src/mono/browser/runtime/loader/config.ts
+++ b/src/mono/browser/runtime/loader/config.ts
@@ -5,7 +5,7 @@ import BuildConfiguration from "consts:configuration";
import WasmEnableThreads from "consts:wasmEnableThreads";
import { type DotnetModuleInternal, type MonoConfigInternal, JSThreadBlockingMode } from "../types/internal";
-import type { BootModule, DotnetModuleConfig, MonoConfig, ResourceGroups, ResourceList } from "../types";
+import type { AssemblyAsset, Assets, BootModule, DotnetModuleConfig, IcuAsset, JsAsset, MonoConfig, PdbAsset, SymbolsAsset, VfsAsset, WasmAsset } from "../types";
import { exportedRuntimeAPI, loaderHelpers, runtimeHelpers } from "./globals";
import { mono_log_error, mono_log_debug } from "./logging";
import { importLibraryInitializers, invokeLibraryInitializers } from "./libraryInitializers";
@@ -24,10 +24,10 @@ export function deep_merge_config (target: MonoConfigInternal, source: MonoConfi
}
if (providedConfig.resources !== undefined) {
providedConfig.resources = deep_merge_resources(target.resources || {
- assembly: {},
- jsModuleNative: {},
- jsModuleRuntime: {},
- wasmNative: {}
+ assembly: [],
+ jsModuleNative: [],
+ jsModuleRuntime: [],
+ wasmNative: []
}, providedConfig.resources);
}
if (providedConfig.environmentVariables !== undefined) {
@@ -51,65 +51,65 @@ export function deep_merge_module (target: DotnetModuleInternal, source: DotnetM
return Object.assign(target, providedConfig);
}
-function deep_merge_resources (target: ResourceGroups, source: ResourceGroups): ResourceGroups {
+function deep_merge_resources (target: Assets, source: Assets): Assets {
// no need to merge the same object
if (target === source) return target;
- const providedResources: ResourceGroups = { ...source };
+ const providedResources: Assets = { ...source };
if (providedResources.assembly !== undefined) {
- providedResources.assembly = { ...(target.assembly || {}), ...(providedResources.assembly || {}) };
+ providedResources.assembly = [...(target.assembly || []), ...(providedResources.assembly || [])];
}
if (providedResources.lazyAssembly !== undefined) {
- providedResources.lazyAssembly = { ...(target.lazyAssembly || {}), ...(providedResources.lazyAssembly || {}) };
+ providedResources.lazyAssembly = [...(target.lazyAssembly || []), ...(providedResources.lazyAssembly || [])];
}
if (providedResources.pdb !== undefined) {
- providedResources.pdb = { ...(target.pdb || {}), ...(providedResources.pdb || {}) };
+ providedResources.pdb = [...(target.pdb || []), ...(providedResources.pdb || [])];
}
if (providedResources.jsModuleWorker !== undefined) {
- providedResources.jsModuleWorker = { ...(target.jsModuleWorker || {}), ...(providedResources.jsModuleWorker || {}) };
+ providedResources.jsModuleWorker = [...(target.jsModuleWorker || []), ...(providedResources.jsModuleWorker || [])];
}
if (providedResources.jsModuleNative !== undefined) {
- providedResources.jsModuleNative = { ...(target.jsModuleNative || {}), ...(providedResources.jsModuleNative || {}) };
+ providedResources.jsModuleNative = [...(target.jsModuleNative || []), ...(providedResources.jsModuleNative || [])];
}
if (providedResources.jsModuleDiagnostics !== undefined) {
- providedResources.jsModuleDiagnostics = { ...(target.jsModuleDiagnostics || {}), ...(providedResources.jsModuleDiagnostics || {}) };
+ providedResources.jsModuleDiagnostics = [...(target.jsModuleDiagnostics || []), ...(providedResources.jsModuleDiagnostics || [])];
}
if (providedResources.jsModuleRuntime !== undefined) {
- providedResources.jsModuleRuntime = { ...(target.jsModuleRuntime || {}), ...(providedResources.jsModuleRuntime || {}) };
+ providedResources.jsModuleRuntime = [...(target.jsModuleRuntime || []), ...(providedResources.jsModuleRuntime || [])];
}
if (providedResources.wasmSymbols !== undefined) {
- providedResources.wasmSymbols = { ...(target.wasmSymbols || {}), ...(providedResources.wasmSymbols || {}) };
+ providedResources.wasmSymbols = [...(target.wasmSymbols || []), ...(providedResources.wasmSymbols || [])];
}
if (providedResources.wasmNative !== undefined) {
- providedResources.wasmNative = { ...(target.wasmNative || {}), ...(providedResources.wasmNative || {}) };
+ providedResources.wasmNative = [...(target.wasmNative || []), ...(providedResources.wasmNative || [])];
}
if (providedResources.icu !== undefined) {
- providedResources.icu = { ...(target.icu || {}), ...(providedResources.icu || {}) };
+ providedResources.icu = [...(target.icu || []), ...(providedResources.icu || [])];
}
if (providedResources.satelliteResources !== undefined) {
- providedResources.satelliteResources = deep_merge_dict(target.satelliteResources || {}, providedResources.satelliteResources || {});
+ providedResources.satelliteResources = deepMergeSatelliteResources(target.satelliteResources || {}, providedResources.satelliteResources || {});
}
if (providedResources.modulesAfterConfigLoaded !== undefined) {
- providedResources.modulesAfterConfigLoaded = { ...(target.modulesAfterConfigLoaded || {}), ...(providedResources.modulesAfterConfigLoaded || {}) };
+ providedResources.modulesAfterConfigLoaded = [...(target.modulesAfterConfigLoaded || []), ...(providedResources.modulesAfterConfigLoaded || [])];
}
if (providedResources.modulesAfterRuntimeReady !== undefined) {
- providedResources.modulesAfterRuntimeReady = { ...(target.modulesAfterRuntimeReady || {}), ...(providedResources.modulesAfterRuntimeReady || {}) };
+ providedResources.modulesAfterRuntimeReady = [...(target.modulesAfterRuntimeReady || []), ...(providedResources.modulesAfterRuntimeReady || [])];
}
if (providedResources.extensions !== undefined) {
providedResources.extensions = { ...(target.extensions || {}), ...(providedResources.extensions || {}) };
}
if (providedResources.vfs !== undefined) {
- providedResources.vfs = deep_merge_dict(target.vfs || {}, providedResources.vfs || {});
+ providedResources.vfs = [...(target.vfs || []), ...(providedResources.vfs || [])];
}
return Object.assign(target, providedResources);
}
-function deep_merge_dict (target: { [key: string]: ResourceList }, source: { [key: string]: ResourceList }) {
+function deepMergeSatelliteResources (target: { [key: string]: AssemblyAsset[] }, source: { [key: string]: AssemblyAsset[] }) {
// no need to merge the same object
if (target === source) return target;
for (const key in source) {
- target[key] = { ...target[key], ...source[key] };
+ target[key] = [...target[key] || [], ...source[key] || []];
}
return target;
}
@@ -122,56 +122,53 @@ export function normalizeConfig () {
config.environmentVariables = config.environmentVariables || {};
config.runtimeOptions = config.runtimeOptions || [];
config.resources = config.resources || {
- assembly: {},
- jsModuleNative: {},
- jsModuleWorker: {},
- jsModuleRuntime: {},
- wasmNative: {},
- vfs: {},
- satelliteResources: {},
+ assembly: [],
+ jsModuleNative: [],
+ jsModuleWorker: [],
+ jsModuleRuntime: [],
+ wasmNative: [],
+ vfs: [],
+ satelliteResources: {}
};
if (config.assets) {
mono_log_debug("config.assets is deprecated, use config.resources instead");
for (const asset of config.assets) {
- const resource = {} as ResourceList;
- resource[asset.name] = asset.hash || "";
- const toMerge = {} as ResourceGroups;
+ const toMerge = {} as Assets;
switch (asset.behavior as string) {
case "assembly":
- toMerge.assembly = resource;
+ toMerge.assembly = [asset as AssemblyAsset];
break;
case "pdb":
- toMerge.pdb = resource;
+ toMerge.pdb = [asset as PdbAsset];
break;
case "resource":
toMerge.satelliteResources = {};
- toMerge.satelliteResources[asset.culture!] = resource;
+ toMerge.satelliteResources[asset.culture!] = [asset as AssemblyAsset];
break;
case "icu":
- toMerge.icu = resource;
+ toMerge.icu = [asset as IcuAsset];
break;
case "symbols":
- toMerge.wasmSymbols = resource;
+ toMerge.wasmSymbols = [asset as SymbolsAsset];
break;
case "vfs":
- toMerge.vfs = {};
- toMerge.vfs[asset.virtualPath!] = resource;
+ toMerge.vfs = [asset as VfsAsset];
break;
case "dotnetwasm":
- toMerge.wasmNative = resource;
+ toMerge.wasmNative = [asset as WasmAsset];
break;
case "js-module-threads":
- toMerge.jsModuleWorker = resource;
+ toMerge.jsModuleWorker = [asset as JsAsset];
break;
case "js-module-runtime":
- toMerge.jsModuleRuntime = resource;
+ toMerge.jsModuleRuntime = [asset as JsAsset];
break;
case "js-module-native":
- toMerge.jsModuleNative = resource;
+ toMerge.jsModuleNative = [asset as JsAsset];
break;
case "js-module-diagnostics":
- toMerge.jsModuleDiagnostics = resource;
+ toMerge.jsModuleDiagnostics = [asset as JsAsset];
break;
case "js-module-dotnet":
// don't merge loader
diff --git a/src/mono/browser/runtime/loader/icu.ts b/src/mono/browser/runtime/loader/icu.ts
index 1e90674a67dcce..4e5b41bff13a24 100644
--- a/src/mono/browser/runtime/loader/icu.ts
+++ b/src/mono/browser/runtime/loader/icu.ts
@@ -5,7 +5,6 @@ import { mono_log_error } from "./logging";
import { GlobalizationMode, MonoConfig } from "../types";
import { ENVIRONMENT_IS_WEB, loaderHelpers } from "./globals";
import { mono_log_info, mono_log_debug } from "./logging";
-import { getNonFingerprintedAssetName } from "./assets";
export function init_globalization () {
loaderHelpers.preferredIcuAsset = getIcuResourceName(loaderHelpers.config);
@@ -48,24 +47,13 @@ export function getIcuResourceName (config: MonoConfig): string | null {
// TODO: when starting on sidecar, we should pass default culture from UI thread
const culture = config.applicationCulture || (ENVIRONMENT_IS_WEB ? (globalThis.navigator && globalThis.navigator.languages && globalThis.navigator.languages[0]) : Intl.DateTimeFormat().resolvedOptions().locale);
- const icuFiles = Object.keys(config.resources.icu);
- const fileMapping: {
- [k: string]: string
- } = {};
- for (let index = 0; index < icuFiles.length; index++) {
- const icuFile = icuFiles[index];
- if (config.resources.fingerprinting) {
- fileMapping[getNonFingerprintedAssetName(icuFile)] = icuFile;
- } else {
- fileMapping[icuFile] = icuFile;
- }
- }
+ const icuFiles = config.resources.icu;
let icuFile = null;
if (config.globalizationMode === GlobalizationMode.Custom) {
// custom ICU file is saved in the resources with fingerprinting and does not require mapping
if (icuFiles.length >= 1) {
- return icuFiles[0];
+ return icuFiles[0].name;
}
} else if (!culture || config.globalizationMode === GlobalizationMode.All) {
icuFile = "icudt.dat";
@@ -73,8 +61,13 @@ export function getIcuResourceName (config: MonoConfig): string | null {
icuFile = getShardedIcuResourceName(culture);
}
- if (icuFile && fileMapping[icuFile]) {
- return fileMapping[icuFile];
+ if (icuFile) {
+ for (let i = 0; i < icuFiles.length; i++) {
+ const asset = icuFiles[i];
+ if (asset.virtualPath === icuFile) {
+ return asset.name;
+ }
+ }
}
}
diff --git a/src/mono/browser/runtime/loader/libraryInitializers.ts b/src/mono/browser/runtime/loader/libraryInitializers.ts
index 4dd0ccfed9ff25..d319d125b0b387 100644
--- a/src/mono/browser/runtime/loader/libraryInitializers.ts
+++ b/src/mono/browser/runtime/loader/libraryInitializers.ts
@@ -5,25 +5,26 @@ import { mono_log_debug, mono_log_warn } from "./logging";
import { appendUniqueQuery } from "./assets";
import { loaderHelpers } from "./globals";
import { mono_exit } from "./exit";
-import { ResourceList } from "../types";
+import { JsAsset } from "../types";
-export async function importLibraryInitializers (libraryInitializers: ResourceList | undefined): Promise {
+export async function importLibraryInitializers (libraryInitializers: JsAsset[] | undefined): Promise {
if (!libraryInitializers) {
return;
}
- const initializerFiles = Object.keys(libraryInitializers);
- await Promise.all(initializerFiles.map(f => importInitializer(f)));
+ await Promise.all((libraryInitializers ?? []).map(i => importInitializer(i!)));
- async function importInitializer (path: string): Promise {
+ async function importInitializer (asset: JsAsset): Promise {
try {
- const adjustedPath = appendUniqueQuery(loaderHelpers.locateFile(path), "js-module-library-initializer");
- mono_log_debug(() => `Attempting to import '${adjustedPath}' for ${path}`);
- const initializer = await import(/*! webpackIgnore: true */ adjustedPath);
-
- loaderHelpers.libraryInitializers!.push({ scriptName: path, exports: initializer });
+ const path = asset.name!;
+ if (!asset.moduleExports) {
+ const adjustedPath = appendUniqueQuery(loaderHelpers.locateFile(path), "js-module-library-initializer");
+ mono_log_debug(() => `Attempting to import '${adjustedPath}' for ${asset}`);
+ asset.moduleExports = await import(/*! webpackIgnore: true */ adjustedPath);
+ }
+ loaderHelpers.libraryInitializers!.push({ scriptName: path, exports: asset.moduleExports });
} catch (error) {
- mono_log_warn(`Failed to import library initializer '${path}': ${error}`);
+ mono_log_warn(`Failed to import library initializer '${asset}': ${error}`);
}
}
}
diff --git a/src/mono/browser/runtime/rollup.config.js b/src/mono/browser/runtime/rollup.config.js
index ec1de7d67b0f75..1966e3dfc7bb9f 100644
--- a/src/mono/browser/runtime/rollup.config.js
+++ b/src/mono/browser/runtime/rollup.config.js
@@ -171,6 +171,7 @@ const loaderConfig = {
format: "es",
file: nativeBinDir + "/dotnet.js",
banner,
+ intro: "/*! bundlerFriendlyImports */",
plugins,
sourcemap: true,
sourcemapPathTransform,
diff --git a/src/mono/browser/runtime/satelliteAssemblies.ts b/src/mono/browser/runtime/satelliteAssemblies.ts
index 713cfb94161f9b..ebbf6d88294bda 100644
--- a/src/mono/browser/runtime/satelliteAssemblies.ts
+++ b/src/mono/browser/runtime/satelliteAssemblies.ts
@@ -3,7 +3,8 @@
import { loaderHelpers } from "./globals";
import { load_satellite_assembly } from "./managed-exports";
-import { AssetEntry } from "./types";
+import type { AssemblyAsset } from "./types";
+import type { AssetEntryInternal } from "./types/internal";
export async function loadSatelliteAssemblies (culturesToLoad: string[]): Promise {
const satelliteResources = loaderHelpers.config.resources!.satelliteResources;
@@ -15,14 +16,10 @@ export async function loadSatelliteAssemblies (culturesToLoad: string[]): Promis
.filter(culture => Object.prototype.hasOwnProperty.call(satelliteResources, culture))
.map(culture => {
const promises: Promise[] = [];
- for (const name in satelliteResources[culture]) {
- const asset: AssetEntry = {
- name,
- hash: satelliteResources[culture][name],
- behavior: "resource",
- culture
- };
-
+ for (let i = 0; i < satelliteResources[culture].length; i++) {
+ const asset = satelliteResources[culture][i] as AssemblyAsset & AssetEntryInternal;
+ asset.behavior = "resource";
+ asset.culture = culture;
promises.push(loaderHelpers.retrieve_asset_download(asset));
}
diff --git a/src/mono/browser/runtime/types/index.ts b/src/mono/browser/runtime/types/index.ts
index e521025b9a0133..74c487753f8cd2 100644
--- a/src/mono/browser/runtime/types/index.ts
+++ b/src/mono/browser/runtime/types/index.ts
@@ -174,10 +174,7 @@ export type MonoConfig = {
*/
applicationCulture?: string,
- /**
- * definition of assets to load along with the runtime.
- */
- resources?: ResourceGroups;
+ resources?: Assets,
/**
* appsettings files to load to VFS
@@ -203,31 +200,95 @@ export type MonoConfig = {
export type ResourceExtensions = { [extensionName: string]: ResourceList };
-export interface ResourceGroups {
+export interface Assets {
hash?: string;
- fingerprinting?: { [name: string]: string },
- coreAssembly?: ResourceList; // nullable only temporarily
- assembly?: ResourceList; // nullable only temporarily
- lazyAssembly?: ResourceList; // nullable only temporarily
- corePdb?: ResourceList;
- pdb?: ResourceList;
-
- jsModuleWorker?: ResourceList;
- jsModuleDiagnostics?: ResourceList;
- jsModuleNative: ResourceList;
- jsModuleRuntime: ResourceList;
- wasmSymbols?: ResourceList;
- wasmNative: ResourceList;
- icu?: ResourceList;
-
- satelliteResources?: { [cultureName: string]: ResourceList };
-
- modulesAfterConfigLoaded?: ResourceList,
- modulesAfterRuntimeReady?: ResourceList
+ coreAssembly?: AssemblyAsset[]; // nullable only temporarily
+ assembly?: AssemblyAsset[]; // nullable only temporarily
+ lazyAssembly?: AssemblyAsset[]; // nullable only temporarily
+ corePdb?: PdbAsset[];
+ pdb?: PdbAsset[];
+
+ jsModuleWorker?: JsAsset[];
+ jsModuleDiagnostics?: JsAsset[];
+ jsModuleNative: JsAsset[];
+ jsModuleRuntime: JsAsset[];
+
+ wasmSymbols?: SymbolsAsset[];
+ wasmNative: WasmAsset[];
+ icu?: IcuAsset[];
+
+ satelliteResources?: { [cultureName: string]: AssemblyAsset[] };
+
+ modulesAfterConfigLoaded?: JsAsset[],
+ modulesAfterRuntimeReady?: JsAsset[]
extensions?: ResourceExtensions
- coreVfs?: { [virtualPath: string]: ResourceList };
- vfs?: { [virtualPath: string]: ResourceList };
+ coreVfs?: VfsAsset[];
+ vfs?: VfsAsset[];
+}
+
+export type Asset = {
+ /**
+ * this should be absolute url to the asset
+ */
+ resolvedUrl?: string;
+ /**
+ * If true, the runtime startup would not fail if the asset download was not successful.
+ */
+ isOptional?: boolean
+ /**
+ * If provided, runtime doesn't have to fetch the data.
+ * Runtime would set the buffer to null after instantiation to free the memory.
+ */
+ buffer?: ArrayBuffer | Promise,
+ /**
+ * It's metadata + fetch-like Promise
+ * If provided, the runtime doesn't have to initiate the download. It would just await the response.
+ */
+ pendingDownload?: LoadingResource
+}
+
+export type WasmAsset = Asset & {
+ name: string;
+ hash?: string | null | "";
+}
+
+export type AssemblyAsset = Asset & {
+ virtualPath: string;
+ name: string; // actually URL
+ hash?: string | null | "";
+}
+
+export type PdbAsset = Asset & {
+ virtualPath: string;
+ name: string; // actually URL
+ hash?: string | null | "";
+}
+
+export type JsAsset = Asset & {
+ /**
+ * If provided, runtime doesn't have to import it's JavaScript modules.
+ * This will not work for multi-threaded runtime.
+ */
+ moduleExports?: any | Promise,
+
+ name?: string; // actually URL
+}
+
+export type SymbolsAsset = Asset & {
+ name: string; // actually URL
+}
+
+export type VfsAsset = Asset & {
+ virtualPath: string;
+ name: string; // actually URL
+ hash?: string | null | "";
+}
+
+export type IcuAsset = Asset & {
+ virtualPath: string;
+ name: string; // actually URL
+ hash?: string | null | "";
}
/**
diff --git a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
index 55403335852af1..b934bf4260d58c 100644
--- a/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
+++ b/src/mono/nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
@@ -215,6 +215,9 @@ Copyright (c) .NET Foundation. All rights reserved.
<_WasmFingerprintBootConfig Condition="'$(_WasmFingerprintBootConfig)' == ''">false
<_WasmPreloadAssets>$(WasmPreloadAssets)
<_WasmPreloadAssets Condition="'$(_WasmPreloadAssets)' == ''">true
+
+ <_WasmBundlerFriendlyBootConfig>$(WasmBundlerFriendlyBootConfig)
+ <_WasmBundlerFriendlyBootConfig Condition="'$(_WasmBundlerFriendlyBootConfig)' == ''">false
$(OutputPath)$(PublishDirName)\
@@ -412,7 +415,8 @@ Copyright (c) .NET Foundation. All rights reserved.
IsPublish="false"
IsAot="$(RunAOTCompilation)"
IsMultiThreaded="$(WasmEnableThreads)"
- FingerprintAssets="$(_WasmFingerprintAssets)" />
+ FingerprintAssets="$(_WasmFingerprintAssets)"
+ BundlerFriendly="$(_WasmBundlerFriendlyBootConfig)" />
@@ -820,7 +824,8 @@ Copyright (c) .NET Foundation. All rights reserved.
IsPublish="true"
IsAot="$(RunAOTCompilation)"
IsMultiThreaded="$(WasmEnableThreads)"
- FingerprintAssets="$(_WasmFingerprintAssets)" />
+ FingerprintAssets="$(_WasmFingerprintAssets)"
+ BundlerFriendly="$(_WasmBundlerFriendlyBootConfig)" />
diff --git a/src/mono/sample/wasm/Directory.Build.targets b/src/mono/sample/wasm/Directory.Build.targets
index dea79fa3bb64cc..3d02ded4c2547e 100644
--- a/src/mono/sample/wasm/Directory.Build.targets
+++ b/src/mono/sample/wasm/Directory.Build.targets
@@ -83,7 +83,7 @@
-
+
diff --git a/src/mono/sample/wasm/browser-advanced/main.js b/src/mono/sample/wasm/browser-advanced/main.js
index 6a22eee4a5c0a8..016a3771aa08c1 100644
--- a/src/mono/sample/wasm/browser-advanced/main.js
+++ b/src/mono/sample/wasm/browser-advanced/main.js
@@ -39,9 +39,9 @@ try {
.withConfig({
maxParallelDownloads: 1,
resources: {
- modulesAfterConfigLoaded: {
- "advanced-sample.lib.module.js": ""
- }
+ modulesAfterConfigLoaded: [{
+ "name": "advanced-sample.lib.module.js"
+ }]
}
})
.withModuleConfig({
diff --git a/src/mono/wasm/Wasm.Build.Tests/AppSettingsTests.cs b/src/mono/wasm/Wasm.Build.Tests/AppSettingsTests.cs
index 3c2da858c29124..11b360a89eb032 100644
--- a/src/mono/wasm/Wasm.Build.Tests/AppSettingsTests.cs
+++ b/src/mono/wasm/Wasm.Build.Tests/AppSettingsTests.cs
@@ -24,23 +24,31 @@ public AppSettingsTests(ITestOutputHelper output, SharedBuildPerTestClassFixture
public static IEnumerable