Skip to content

Commit 1f5e355

Browse files
authored
[Blazor] Javascript bundler friendly (#62417)
1 parent ee050bd commit 1f5e355

File tree

7 files changed

+58
-17
lines changed

7 files changed

+58
-17
lines changed

src/Components/Web.JS/rollup.config.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ export default createBaseConfig({
1414
},
1515
dir: __dirname,
1616
updateConfig: (config, environment, _, input) => {
17+
config.plugins.push({
18+
name: 'Resolve dotnet.js dynamic import',
19+
resolveDynamicImport(source, importer) {
20+
if (source === './dotnet.js') {
21+
return { id: './dotnet.js', moduleSideEffects: false, external: 'relative' };
22+
}
23+
return null;
24+
}
25+
});
26+
1727
if (input.includes("WebView")) {
1828
config.output.sourcemap = 'inline';
1929
} else {

src/Components/Web.JS/src/JSInitializers/JSInitializers.Server.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import { CircuitStartOptions } from '../Platform/Circuits/CircuitStartOptions';
55
import { WebRendererId } from '../Rendering/WebRendererId';
6-
import { JSInitializer } from './JSInitializers';
6+
import { JSAsset, JSInitializer } from './JSInitializers';
77

88
export async function fetchAndInvokeInitializers(options: Partial<CircuitStartOptions>) : Promise<JSInitializer> {
99
if (options.initializers) {
@@ -19,7 +19,9 @@ export async function fetchAndInvokeInitializers(options: Partial<CircuitStartOp
1919
cache: 'no-cache',
2020
});
2121

22-
const initializers: string[] = await jsInitializersResponse.json();
22+
const initializers = (await jsInitializersResponse.json()).map(name => ({
23+
name,
24+
})) as JSAsset[];
2325
const jsInitializer = new JSInitializer(/* singleRuntime: */ true, undefined, undefined, WebRendererId.Server);
2426
await jsInitializer.importInitializersAsync(initializers, [options]);
2527
return jsInitializer;

src/Components/Web.JS/src/JSInitializers/JSInitializers.Web.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44
import { Logger } from '../Platform/Logging/Logger';
55
import { WebStartOptions } from '../Platform/WebStartOptions';
66
import { discoverWebInitializers } from '../Services/ComponentDescriptorDiscovery';
7-
import { JSInitializer } from './JSInitializers';
7+
import { JSAsset, JSInitializer } from './JSInitializers';
88

99
export async function fetchAndInvokeInitializers(options: Partial<WebStartOptions>, logger: Logger) : Promise<JSInitializer> {
1010
const initializersElement = discoverWebInitializers(document);
1111
if (!initializersElement) {
1212
return new JSInitializer(false, logger);
1313
}
14-
const initializers: string[] = JSON.parse(atob(initializersElement)) as string[] ?? [];
14+
const initializers = (JSON.parse(atob(initializersElement)) as string[] ?? []).map(name => ({
15+
name,
16+
})) as JSAsset[];
1517
const jsInitializer = new JSInitializer(false, logger);
1618
await jsInitializer.importInitializersAsync(initializers, [options]);
1719
return jsInitializer;

src/Components/Web.JS/src/JSInitializers/JSInitializers.WebAssembly.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { MonoConfig } from '@microsoft/dotnet-runtime';
55
import { WebAssemblyStartOptions } from '../Platform/WebAssemblyStartOptions';
66
import { WebRendererId } from '../Rendering/WebRendererId';
7-
import { JSInitializer } from './JSInitializers';
7+
import { JSAsset, JSInitializer } from './JSInitializers';
88

99
export async function fetchAndInvokeInitializers(options: Partial<WebAssemblyStartOptions>, loadedConfig: MonoConfig): Promise<JSInitializer> {
1010
if (options.initializers) {
@@ -20,7 +20,22 @@ export async function fetchAndInvokeInitializers(options: Partial<WebAssemblySta
2020
undefined,
2121
WebRendererId.WebAssembly
2222
);
23-
const initializers = Object.keys((loadedConfig?.resources?.['libraryInitializers']) || {});
23+
24+
const configInitializers = loadedConfig?.resources?.['libraryInitializers'];
25+
let initializers : JSAsset[];
26+
if (!configInitializers) {
27+
initializers = [];
28+
}
29+
else if ("length" in configInitializers) {
30+
// New boot config schema.
31+
initializers = configInitializers;
32+
} else {
33+
// Old boot config schema.
34+
initializers = Object.keys(configInitializers).map(name => ({
35+
name,
36+
})) as JSAsset[];
37+
}
38+
2439
await jsInitializer.importInitializersAsync(initializers, initializerArguments);
2540
return jsInitializer;
2641
}

src/Components/Web.JS/src/JSInitializers/JSInitializers.WebView.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
import { JSInitializer } from './JSInitializers';
4+
import { JSAsset, JSInitializer } from './JSInitializers';
55

66
export async function fetchAndInvokeInitializers() : Promise<JSInitializer> {
77
const jsInitializersResponse = await fetch('_framework/blazor.modules.json', {
@@ -10,7 +10,9 @@ export async function fetchAndInvokeInitializers() : Promise<JSInitializer> {
1010
cache: 'no-cache',
1111
});
1212

13-
const initializers: string[] = await jsInitializersResponse.json();
13+
const initializers = (await jsInitializersResponse.json()).map(name => ({
14+
name,
15+
})) as JSAsset[];
1416
const jsInitializer = new JSInitializer();
1517
await jsInitializer.importInitializersAsync(initializers, []);
1618
return jsInitializer;

src/Components/Web.JS/src/JSInitializers/JSInitializers.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ export type BlazorInitializer = {
2424
afterServerStarted: AfterBlazorServerStartedCallback,
2525
};
2626

27+
export type JSAsset = {
28+
moduleExports?: any | Promise<any>,
29+
name?: string; // actually URL
30+
}
31+
2732
export class JSInitializer {
2833
private afterStartedCallbacks: AfterBlazorStartedCallback[] = [];
2934

@@ -38,7 +43,7 @@ export class JSInitializer {
3843
}
3944
}
4045

41-
async importInitializersAsync(initializerFiles: string[], initializerArguments: unknown[]): Promise<void> {
46+
async importInitializersAsync(initializerFiles: JSAsset[], initializerArguments: unknown[]): Promise<void> {
4247
// This code is not called on WASM, because library intializers are imported by runtime.
4348

4449
await Promise.all(initializerFiles.map(f => importAndInvokeInitializer(this, f)));
@@ -50,9 +55,14 @@ export class JSInitializer {
5055
return path;
5156
}
5257

53-
async function importAndInvokeInitializer(jsInitializer: JSInitializer, path: string): Promise<void> {
54-
const adjustedPath = adjustPath(path);
55-
const initializer = await import(/* webpackIgnore: true */ adjustedPath) as Partial<BlazorInitializer>;
58+
async function importAndInvokeInitializer(jsInitializer: JSInitializer, asset: JSAsset): Promise<void> {
59+
let adjustedPath;
60+
if (!asset.moduleExports) {
61+
adjustedPath = adjustPath(asset.name!);
62+
asset.moduleExports = await import(/* webpackIgnore: true */ adjustedPath);
63+
}
64+
65+
const initializer = asset.moduleExports as Partial<BlazorInitializer>;
5666
if (initializer === undefined) {
5767
return;
5868
}

src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,22 +116,22 @@ async function importDotnetJs(startOptions: Partial<WebAssemblyStartOptions>): P
116116
throw new Error('This browser does not support WebAssembly.');
117117
}
118118

119-
let src = '_framework/dotnet.js';
120119

121120
// Allow overriding the URI from which the dotnet.*.js file is loaded
122121
if (startOptions.loadBootResource) {
123122
const resourceType: WebAssemblyBootResourceType = 'dotnetjs';
124-
const customSrc = startOptions.loadBootResource(resourceType, 'dotnet.js', src, '', 'js-module-dotnet');
123+
const customSrc = startOptions.loadBootResource(resourceType, 'dotnet.js', '_framework/dotnet.js', '', 'js-module-dotnet');
125124
if (typeof (customSrc) === 'string') {
126-
src = customSrc;
125+
const absoluteSrc = (new URL(customSrc, document.baseURI)).toString();
126+
return await import(/* webpackIgnore: true */ absoluteSrc);
127127
} else if (customSrc) {
128128
// Since we must load this via a import, it's only valid to supply a URI (and not a Request, say)
129129
throw new Error(`For a ${resourceType} resource, custom loaders must supply a URI string.`);
130130
}
131131
}
132132

133-
const absoluteSrc = (new URL(src, document.baseURI)).toString();
134-
return await import(/* webpackIgnore: true */ absoluteSrc);
133+
// @ts-ignore: This dynamic import is handled at runtime and does not need a type declaration.
134+
return await import(/* webpackIgnore: true */ "./dotnet.js");
135135
}
136136

137137
function prepareRuntimeConfig(options: Partial<WebAssemblyStartOptions>, onConfigLoadedCallback?: (loadedConfig: MonoConfig) => void): DotnetModuleConfig {

0 commit comments

Comments
 (0)