Make Blazor apps actually start up Mono and execute the specified .NET entrypoint
This commit is contained in:
parent
4d764d78df
commit
4138b3a049
|
|
@ -40,6 +40,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostedInAspNet.Client", "sa
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostedInAspNet.Server", "samples\HostedInAspNet.Server\HostedInAspNet.Server.csproj", "{F8996835-41F7-4663-91DF-3B5652ADC37D}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostedInAspNet.Server", "samples\HostedInAspNet.Server\HostedInAspNet.Server.csproj", "{F8996835-41F7-4663-91DF-3B5652ADC37D}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Blazor", "src\Microsoft.Blazor\Microsoft.Blazor.csproj", "{7FD8C650-74B3-4153-AEA1-00F4F6AF393D}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -94,6 +96,10 @@ Global
|
||||||
{F8996835-41F7-4663-91DF-3B5652ADC37D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{F8996835-41F7-4663-91DF-3B5652ADC37D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{F8996835-41F7-4663-91DF-3B5652ADC37D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{F8996835-41F7-4663-91DF-3B5652ADC37D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{F8996835-41F7-4663-91DF-3B5652ADC37D}.Release|Any CPU.Build.0 = Release|Any CPU
|
{F8996835-41F7-4663-91DF-3B5652ADC37D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7FD8C650-74B3-4153-AEA1-00F4F6AF393D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7FD8C650-74B3-4153-AEA1-00F4F6AF393D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7FD8C650-74B3-4153-AEA1-00F4F6AF393D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7FD8C650-74B3-4153-AEA1-00F4F6AF393D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -113,6 +119,7 @@ Global
|
||||||
{4D367450-96E9-4C8C-8B56-EED8ADE3A20D} = {F5FDD4E5-6A52-4A86-BE5E-5E42CB1DC8DA}
|
{4D367450-96E9-4C8C-8B56-EED8ADE3A20D} = {F5FDD4E5-6A52-4A86-BE5E-5E42CB1DC8DA}
|
||||||
{B4335F7C-4E86-4559-821F-F1B1C75F5FAE} = {4D367450-96E9-4C8C-8B56-EED8ADE3A20D}
|
{B4335F7C-4E86-4559-821F-F1B1C75F5FAE} = {4D367450-96E9-4C8C-8B56-EED8ADE3A20D}
|
||||||
{F8996835-41F7-4663-91DF-3B5652ADC37D} = {4D367450-96E9-4C8C-8B56-EED8ADE3A20D}
|
{F8996835-41F7-4663-91DF-3B5652ADC37D} = {4D367450-96E9-4C8C-8B56-EED8ADE3A20D}
|
||||||
|
{7FD8C650-74B3-4153-AEA1-00F4F6AF393D} = {B867E038-B3CE-43E3-9292-61568C46CDEB}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {504DA352-6788-4DC0-8705-82167E72A4D3}
|
SolutionGuid = {504DA352-6788-4DC0-8705-82167E72A4D3}
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,8 @@
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Microsoft.Blazor\Microsoft.Blazor.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Hello</h1>
|
<h1>Hello</h1>
|
||||||
<script src="/_framework/blazor.js"></script>
|
<script src="/_framework/blazor.js" main="HostedInAspNet.Client.dll"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1 +1,35 @@
|
||||||
console.log('Blazor is loading');
|
import { platform } from './Environment';
|
||||||
|
import { getAssemblyNameFromUrl } from './Platform/DotNet';
|
||||||
|
|
||||||
|
async function boot() {
|
||||||
|
// Read startup config from the <script> element that's importing this file
|
||||||
|
const allScriptElems = document.getElementsByTagName('script');
|
||||||
|
const thisScriptElem = allScriptElems[allScriptElems.length - 1];
|
||||||
|
const entryPoint = thisScriptElem.getAttribute('main');
|
||||||
|
if (!entryPoint) {
|
||||||
|
throw new Error('Missing "main" attribute on Blazor script tag.');
|
||||||
|
}
|
||||||
|
const entryPointAssemblyName = getAssemblyNameFromUrl(entryPoint);
|
||||||
|
const referenceAssembliesCommaSeparated = thisScriptElem.getAttribute('references') || '';
|
||||||
|
const referenceAssemblies = referenceAssembliesCommaSeparated
|
||||||
|
.split(',')
|
||||||
|
.map(s => s.trim())
|
||||||
|
.filter(s => !!s);
|
||||||
|
|
||||||
|
// Determine the URLs of the assemblies we want to load
|
||||||
|
const loadAssemblyUrls = [entryPoint]
|
||||||
|
.concat(referenceAssemblies) // Developer-specified references
|
||||||
|
.concat(['Microsoft.Blazor.dll']) // Standard references
|
||||||
|
.map(filename => `/_bin/${filename}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await platform.start(loadAssemblyUrls);
|
||||||
|
} catch (ex) {
|
||||||
|
throw new Error(`Failed to start platform. Reason: ${ex}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start up the application
|
||||||
|
platform.callEntryPoint(entryPointAssemblyName, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
boot();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Expose an export called 'platform' of the interface type 'Platform',
|
||||||
|
// so that consumers can be agnostic about which implementation they use.
|
||||||
|
// Basic alternative to having an actual DI container.
|
||||||
|
import { Platform } from './Platform/Platform';
|
||||||
|
import { monoPlatform } from './Platform/Mono/MonoPlatform';
|
||||||
|
export const platform: Platform = monoPlatform;
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
export function getAssemblyNameFromUrl(url: string) {
|
||||||
|
const lastSegment = url.substring(url.lastIndexOf('/') + 1);
|
||||||
|
const queryStringStartPos = lastSegment.indexOf('?');
|
||||||
|
const filename = queryStringStartPos < 0 ? lastSegment : lastSegment.substring(0, queryStringStartPos);
|
||||||
|
return filename.replace(/\.dll$/, '');
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,186 @@
|
||||||
|
import { MethodHandle, System_Object, System_String, Platform } from '../Platform';
|
||||||
|
import { getAssemblyNameFromUrl } from '../DotNet';
|
||||||
|
|
||||||
|
let assembly_load: (assemblyName: string) => number;
|
||||||
|
let find_class: (assemblyHandle: number, namespace: string, className: string) => number;
|
||||||
|
let find_method: (typeHandle: number, methodName: string, unknownArg: number) => MethodHandle;
|
||||||
|
let invoke_method: (method: MethodHandle, target: System_Object, argsArrayPtr: number, exceptionFlagIntPtr: number) => System_Object;
|
||||||
|
let mono_string_get_utf8: (managedString: System_String) => Mono.Utf8Ptr;
|
||||||
|
let mono_string: (jsString: string) => System_String;
|
||||||
|
|
||||||
|
export const monoPlatform: Platform = {
|
||||||
|
start: function start(loadAssemblyUrls: string[]) {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
// mono.js assumes the existence of this
|
||||||
|
window['Browser'] = {
|
||||||
|
init: () => { },
|
||||||
|
asyncLoad: asyncLoad
|
||||||
|
};
|
||||||
|
|
||||||
|
// Emscripten works by expecting the module config to be a global
|
||||||
|
window['Module'] = createEmscriptenModuleInstance(loadAssemblyUrls, resolve, reject);
|
||||||
|
|
||||||
|
addScriptTagsToDocument();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
findMethod: function findMethod(assemblyName: string, namespace: string, className: string, methodName: string): MethodHandle {
|
||||||
|
// TODO: Cache the assembly_load outputs?
|
||||||
|
const assemblyHandle = assembly_load(assemblyName);
|
||||||
|
if (!assemblyHandle) {
|
||||||
|
throw new Error(`Could not find assembly "${assemblyName}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeHandle = find_class(assemblyHandle, namespace, className);
|
||||||
|
if (!typeHandle) {
|
||||||
|
throw new Error(`Could not find type "${className}'" in namespace "${namespace}" in assembly "${assemblyName}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const methodHandle = find_method(typeHandle, methodName, -1);
|
||||||
|
if (!methodHandle) {
|
||||||
|
throw new Error(`Could not find method "${methodName}" on type "${namespace}.${className}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return methodHandle;
|
||||||
|
},
|
||||||
|
|
||||||
|
callEntryPoint: function callEntryPoint(assemblyName: string, args: System_Object[]): void {
|
||||||
|
// TODO: There should be a proper way of running whatever counts as the entrypoint without
|
||||||
|
// having to specify what method it is, but I haven't found it. The code here assumes
|
||||||
|
// that the entry point is "<assemblyname>.Program.Main" (i.e., namespace == assembly name).
|
||||||
|
const entryPointMethod = monoPlatform.findMethod(assemblyName, assemblyName, 'Program', 'Main');
|
||||||
|
monoPlatform.callMethod(entryPointMethod, null, args);
|
||||||
|
},
|
||||||
|
|
||||||
|
callMethod: function callMethod(method: MethodHandle, target: System_Object, args: System_Object[]): System_Object {
|
||||||
|
const stack = Module.Runtime.stackSave();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const argsBuffer = Module.Runtime.stackAlloc(args.length);
|
||||||
|
const exceptionFlagManagedInt = Module.Runtime.stackAlloc(4);
|
||||||
|
for (var i = 0; i < args.length; ++i) {
|
||||||
|
Module.setValue(argsBuffer + i * 4, args[i], 'i32');
|
||||||
|
}
|
||||||
|
Module.setValue(exceptionFlagManagedInt, 0, 'i32');
|
||||||
|
|
||||||
|
const res = invoke_method(method, target, argsBuffer, exceptionFlagManagedInt);
|
||||||
|
|
||||||
|
if (Module.getValue(exceptionFlagManagedInt, 'i32') !== 0) {
|
||||||
|
// If the exception flag is set, the returned value is exception.ToString()
|
||||||
|
throw new Error(monoPlatform.toJavaScriptString(<System_String>res));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} finally {
|
||||||
|
Module.Runtime.stackRestore(stack);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toJavaScriptString: function toJavaScriptString(managedString: System_String) {
|
||||||
|
// Comments from original Mono sample:
|
||||||
|
//FIXME this is wastefull, we could remove the temp malloc by going the UTF16 route
|
||||||
|
//FIXME this is unsafe, cuz raw objects could be GC'd.
|
||||||
|
|
||||||
|
if (!managedString) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const utf8 = mono_string_get_utf8(managedString);
|
||||||
|
const res = Module.UTF8ToString(utf8);
|
||||||
|
Module._free(utf8 as any);
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
|
||||||
|
toDotNetString: function toDotNetString(jsString: string): System_String {
|
||||||
|
return mono_string(jsString);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function addScriptTagsToDocument() {
|
||||||
|
// Load either the wasm or asm.js version of the Mono runtime
|
||||||
|
const browserSupportsNativeWebAssembly = typeof WebAssembly !== 'undefined' && WebAssembly.validate;
|
||||||
|
const monoRuntimeUrlBase = '/_framework/' + (browserSupportsNativeWebAssembly ? 'wasm' : 'asmjs');
|
||||||
|
const monoRuntimeScriptUrl = `${monoRuntimeUrlBase}/mono.js`;
|
||||||
|
|
||||||
|
if (!browserSupportsNativeWebAssembly) {
|
||||||
|
// In the asmjs case, the initial memory structure is in a separate file we need to download
|
||||||
|
const meminitXHR = Module['memoryInitializerRequest'] = new XMLHttpRequest();
|
||||||
|
meminitXHR.open('GET', `${monoRuntimeUrlBase}/mono.js.mem`);
|
||||||
|
meminitXHR.responseType = 'arraybuffer';
|
||||||
|
meminitXHR.send(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.write(`<script defer src="${monoRuntimeScriptUrl}"></script>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEmscriptenModuleInstance(loadAssemblyUrls: string[], onReady: () => void, onError: (reason: any) => void) {
|
||||||
|
const module = {} as typeof Module;
|
||||||
|
|
||||||
|
module.print = line => console.log(`WASM: ${line}`);
|
||||||
|
module.printErr = line => console.error(`WASM: ${line}`);
|
||||||
|
module.wasmBinaryFile = '/_framework/wasm/mono.wasm';
|
||||||
|
module.asmjsCodeFile = '/_framework/asmjs/mono.asm.js';
|
||||||
|
module.preRun = [];
|
||||||
|
module.postRun = [];
|
||||||
|
module.preloadPlugins = [];
|
||||||
|
|
||||||
|
module.preRun.push(() => {
|
||||||
|
// By now, emscripten should be initialised enough that we can capture these methods for later use
|
||||||
|
assembly_load = Module.cwrap('mono_wasm_assembly_load', 'number', ['string']);
|
||||||
|
find_class = Module.cwrap('mono_wasm_assembly_find_class', 'number', ['number', 'string', 'string']);
|
||||||
|
find_method = Module.cwrap('mono_wasm_assembly_find_method', 'number', ['number', 'string', 'number']);
|
||||||
|
invoke_method = Module.cwrap('mono_wasm_invoke_method', 'number', ['number', 'number', 'number']);
|
||||||
|
mono_string_get_utf8 = Module.cwrap('mono_wasm_string_get_utf8', 'number', ['number']);
|
||||||
|
mono_string = Module.cwrap('mono_wasm_string_from_js', 'number', ['string']);
|
||||||
|
|
||||||
|
const loadBclAssemblies = [
|
||||||
|
'mscorlib',
|
||||||
|
'System',
|
||||||
|
'System.Core',
|
||||||
|
'Facades/netstandard',
|
||||||
|
'Facades/System.Console',
|
||||||
|
'Facades/System.Collections',
|
||||||
|
'Facades/System.Diagnostics.Debug',
|
||||||
|
'Facades/System.IO',
|
||||||
|
'Facades/System.Linq',
|
||||||
|
'Facades/System.Reflection',
|
||||||
|
'Facades/System.Reflection.Extensions',
|
||||||
|
'Facades/System.Runtime',
|
||||||
|
'Facades/System.Runtime.Extensions',
|
||||||
|
'Facades/System.Runtime.InteropServices',
|
||||||
|
'Facades/System.Threading',
|
||||||
|
'Facades/System.Threading.Tasks'
|
||||||
|
];
|
||||||
|
|
||||||
|
var allAssemblyUrls = loadAssemblyUrls
|
||||||
|
.concat(loadBclAssemblies.map(name => `_framework/bcl/${name}.dll`));
|
||||||
|
|
||||||
|
Module.FS_createPath('/', 'appBinDir', true, true);
|
||||||
|
allAssemblyUrls.forEach(url =>
|
||||||
|
FS.createPreloadedFile('appBinDir', `${getAssemblyNameFromUrl(url)}.dll`, url, true, false, null, <any>onError));
|
||||||
|
});
|
||||||
|
|
||||||
|
module.postRun.push(() => {
|
||||||
|
const load_runtime = Module.cwrap('mono_wasm_load_runtime', null, ['string']);
|
||||||
|
load_runtime('appBinDir');
|
||||||
|
onReady();
|
||||||
|
});
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
function asyncLoad(url, onload, onerror) {
|
||||||
|
var xhr = new XMLHttpRequest;
|
||||||
|
xhr.open('GET', url, /* async: */ true);
|
||||||
|
xhr.responseType = 'arraybuffer';
|
||||||
|
xhr.onload = function xhr_onload() {
|
||||||
|
if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
|
||||||
|
var asm = new Uint8Array(xhr.response);
|
||||||
|
onload(asm);
|
||||||
|
} else {
|
||||||
|
onerror(xhr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.onerror = onerror;
|
||||||
|
xhr.send(null);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
declare namespace Module {
|
||||||
|
function UTF8ToString(utf8: Mono.Utf8Ptr): string;
|
||||||
|
var preloadPlugins: any[];
|
||||||
|
|
||||||
|
// These should probably be in @types/emscripten
|
||||||
|
var wasmBinaryFile: string;
|
||||||
|
var asmjsCodeFile: string;
|
||||||
|
function FS_createPath(parent, path, canRead, canWrite);
|
||||||
|
function FS_createDataFile(parent, name, data, canRead, canWrite, canOwn);
|
||||||
|
}
|
||||||
|
|
||||||
|
declare namespace Mono {
|
||||||
|
interface Utf8Ptr { Utf8Ptr__DO_NOT_IMPLEMENT: any }
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
export interface Platform {
|
||||||
|
start(loadAssemblyUrls: string[]): Promise<void>;
|
||||||
|
|
||||||
|
callEntryPoint(assemblyName: string, args: System_Object[]);
|
||||||
|
findMethod(assemblyName: string, namespace: string, className: string, methodName: string): MethodHandle;
|
||||||
|
callMethod(method: MethodHandle, target: System_Object, args: System_Object[]): System_Object;
|
||||||
|
|
||||||
|
toJavaScriptString(dotNetString: System_String): string;
|
||||||
|
toDotNetString(javaScriptString: string): System_String;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't actually instantiate any of these at runtime. For perf it's preferable to
|
||||||
|
// use the original 'number' instances without any boxing. The definitions are just
|
||||||
|
// for compile-time checking, since TypeScript doesn't support nominal types.
|
||||||
|
export interface MethodHandle { MethodHandle__DO_NOT_IMPLEMENT: any };
|
||||||
|
export interface System_Object { System_Object__DO_NOT_IMPLEMENT: any };
|
||||||
|
export interface System_String extends System_Object { System_String__DO_NOT_IMPLEMENT: any }
|
||||||
|
|
@ -2,9 +2,13 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net.Mime;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Builder
|
namespace Microsoft.AspNetCore.Builder
|
||||||
{
|
{
|
||||||
|
|
@ -18,12 +22,40 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
var sourcePath = Path.Combine(env.ContentRootPath, relativeSourcePath);
|
var sourcePath = Path.Combine(env.ContentRootPath, relativeSourcePath);
|
||||||
|
|
||||||
ServeWebRoot(applicationBuilder, sourcePath);
|
ServeWebRoot(applicationBuilder, sourcePath);
|
||||||
|
ServeClientBinDir(applicationBuilder, sourcePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ServeWebRoot(IApplicationBuilder applicationBuilder, string sourcePath)
|
private static void ServeClientBinDir(IApplicationBuilder applicationBuilder, string clientAppSourceRoot)
|
||||||
|
{
|
||||||
|
var clientBinDirPath = FindClientBinDir(clientAppSourceRoot);
|
||||||
|
applicationBuilder.UseStaticFiles(new StaticFileOptions
|
||||||
|
{
|
||||||
|
RequestPath = "/_bin",
|
||||||
|
FileProvider = new PhysicalFileProvider(clientBinDirPath),
|
||||||
|
ContentTypeProvider = new FileExtensionContentTypeProvider(new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ ".dll", MediaTypeNames.Application.Octet },
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FindClientBinDir(string clientAppSourceRoot)
|
||||||
|
{
|
||||||
|
var binDebugDir = Path.Combine(clientAppSourceRoot, "bin", "Debug");
|
||||||
|
var subdirectories = Directory.GetDirectories(binDebugDir);
|
||||||
|
if (subdirectories.Length != 1)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Could not locate bin directory for Blazor app. " +
|
||||||
|
$"Expected to find exactly 1 subdirectory in '{binDebugDir}', but found {subdirectories.Length}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Path.Combine(binDebugDir, subdirectories[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ServeWebRoot(IApplicationBuilder applicationBuilder, string clientAppSourceRoot)
|
||||||
{
|
{
|
||||||
var webRootFileProvider = new PhysicalFileProvider(
|
var webRootFileProvider = new PhysicalFileProvider(
|
||||||
Path.Combine(sourcePath, "wwwroot"));
|
Path.Combine(clientAppSourceRoot, "wwwroot"));
|
||||||
|
|
||||||
applicationBuilder.UseDefaultFiles(new DefaultFilesOptions
|
applicationBuilder.UseDefaultFiles(new DefaultFilesOptions
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
Loading…
Reference in New Issue