Add support for configuration (#19544)
* Add support for configuration Fixes https://github.com/dotnet/aspnetcore/issues/18675
This commit is contained in:
parent
0006fdf093
commit
147c39289a
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -4,10 +4,12 @@ import * as Environment from './Environment';
|
||||||
import { monoPlatform } from './Platform/Mono/MonoPlatform';
|
import { monoPlatform } from './Platform/Mono/MonoPlatform';
|
||||||
import { renderBatch } from './Rendering/Renderer';
|
import { renderBatch } from './Rendering/Renderer';
|
||||||
import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch';
|
import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch';
|
||||||
import { Pointer } from './Platform/Platform';
|
|
||||||
import { shouldAutoStart } from './BootCommon';
|
import { shouldAutoStart } from './BootCommon';
|
||||||
import { setEventDispatcher } from './Rendering/RendererEventDispatcher';
|
import { setEventDispatcher } from './Rendering/RendererEventDispatcher';
|
||||||
import { WebAssemblyResourceLoader } from './Platform/WebAssemblyResourceLoader';
|
import { WebAssemblyResourceLoader } from './Platform/WebAssemblyResourceLoader';
|
||||||
|
import { WebAssemblyConfigLoader } from './Platform/WebAssemblyConfigLoader';
|
||||||
|
import { BootConfigResult } from './Platform/BootConfig';
|
||||||
|
import { Pointer } from './Platform/Platform';
|
||||||
|
|
||||||
let started = false;
|
let started = false;
|
||||||
|
|
||||||
|
|
@ -38,7 +40,12 @@ async function boot(options?: any): Promise<void> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch the resources and prepare the Mono runtime
|
// Fetch the resources and prepare the Mono runtime
|
||||||
const resourceLoader = await WebAssemblyResourceLoader.initAsync();
|
const bootConfigResult = await BootConfigResult.initAsync();
|
||||||
|
|
||||||
|
const [resourceLoader] = await Promise.all([
|
||||||
|
WebAssemblyResourceLoader.initAsync(bootConfigResult.bootConfig),
|
||||||
|
WebAssemblyConfigLoader.initAsync(bootConfigResult)]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await platform.start(resourceLoader);
|
await platform.start(resourceLoader);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
export class BootConfigResult {
|
||||||
|
private constructor(public bootConfig: BootJsonData, public applicationEnvironment: string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static async initAsync(): Promise<BootConfigResult> {
|
||||||
|
const bootConfigResponse = await fetch('_framework/blazor.boot.json', {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
|
cache: 'no-cache'
|
||||||
|
});
|
||||||
|
|
||||||
|
// While we can expect an ASP.NET Core hosted application to include the environment, other
|
||||||
|
// hosts may not. Assume 'Production' in the absenc of any specified value.
|
||||||
|
const applicationEnvironment = bootConfigResponse.headers.get('Blazor-Environment') || 'Production';
|
||||||
|
const bootConfig: BootJsonData = await bootConfigResponse.json();
|
||||||
|
|
||||||
|
return new BootConfigResult(bootConfig, applicationEnvironment);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep in sync with bootJsonData in Microsoft.AspNetCore.Components.WebAssembly.Build
|
||||||
|
export interface BootJsonData {
|
||||||
|
readonly entryAssembly: string;
|
||||||
|
readonly resources: ResourceGroups;
|
||||||
|
readonly debugBuild: boolean;
|
||||||
|
readonly linkerEnabled: boolean;
|
||||||
|
readonly cacheBootResources: boolean;
|
||||||
|
readonly config: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResourceGroups {
|
||||||
|
readonly assembly: ResourceList;
|
||||||
|
readonly pdb?: ResourceList;
|
||||||
|
readonly runtime: ResourceList;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ResourceList = { [name: string]: string };
|
||||||
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { System_Object, System_String, System_Array, Pointer, Platform } from '../Platform';
|
|
||||||
import { attachDebuggerHotkey, hasDebuggingEnabled } from './MonoDebugger';
|
import { attachDebuggerHotkey, hasDebuggingEnabled } from './MonoDebugger';
|
||||||
import { showErrorNotification } from '../../BootErrors';
|
import { showErrorNotification } from '../../BootErrors';
|
||||||
import { WebAssemblyResourceLoader, LoadingResource } from '../WebAssemblyResourceLoader';
|
import { WebAssemblyResourceLoader, LoadingResource } from '../WebAssemblyResourceLoader';
|
||||||
|
import { Platform, System_Array, Pointer, System_Object, System_String } from '../Platform';
|
||||||
|
|
||||||
let mono_string_get_utf8: (managedString: System_String) => Mono.Utf8Ptr;
|
let mono_string_get_utf8: (managedString: System_String) => Pointer;
|
||||||
let mono_wasm_add_assembly: (name: string, heapAddress: number, length: number) => void;
|
let mono_wasm_add_assembly: (name: string, heapAddress: number, length: number) => void;
|
||||||
const appBinDirName = 'appBinDir';
|
const appBinDirName = 'appBinDir';
|
||||||
const uint64HighOrderShift = Math.pow(2, 32);
|
const uint64HighOrderShift = Math.pow(2, 32);
|
||||||
|
|
@ -47,7 +47,7 @@ export const monoPlatform: Platform = {
|
||||||
// FIXME this is unsafe, cuz raw objects could be GC'd.
|
// FIXME this is unsafe, cuz raw objects could be GC'd.
|
||||||
|
|
||||||
const utf8 = mono_string_get_utf8(managedString);
|
const utf8 = mono_string_get_utf8(managedString);
|
||||||
const res = Module.UTF8ToString(utf8);
|
const res = (<any>window['Module']).UTF8ToString(utf8);
|
||||||
Module._free(utf8 as any);
|
Module._free(utf8 as any);
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
|
@ -168,7 +168,7 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade
|
||||||
};
|
};
|
||||||
module.preRun = [];
|
module.preRun = [];
|
||||||
module.postRun = [];
|
module.postRun = [];
|
||||||
module.preloadPlugins = [];
|
(module as any).preloadPlugins = [];
|
||||||
|
|
||||||
// Override the mechanism for fetching the main wasm file so we can connect it to our cache
|
// Override the mechanism for fetching the main wasm file so we can connect it to our cache
|
||||||
module.instantiateWasm = (imports, successCallback): WebAssembly.Exports => {
|
module.instantiateWasm = (imports, successCallback): WebAssembly.Exports => {
|
||||||
|
|
@ -258,10 +258,10 @@ function getArrayDataPointer<T>(array: System_Array<T>): number {
|
||||||
return <number><any>array + 12; // First byte from here is length, then following bytes are entries
|
return <number><any>array + 12; // First byte from here is length, then following bytes are entries
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindStaticMethod(assembly: string, typeName: string, method: string) : (...args: any[]) => any {
|
function bindStaticMethod(assembly: string, typeName: string, method: string) {
|
||||||
// Fully qualified name looks like this: "[debugger-test] Math:IntAdd"
|
// Fully qualified name looks like this: "[debugger-test] Math:IntAdd"
|
||||||
const fqn = `[${assembly}] ${typeName}:${method}`;
|
const fqn = `[${assembly}] ${typeName}:${method}`;
|
||||||
return Module.mono_bind_static_method(fqn);
|
return BINDING.bind_static_method(fqn);
|
||||||
}
|
}
|
||||||
|
|
||||||
function attachInteropInvoker(): void {
|
function attachInteropInvoker(): void {
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
declare namespace Module {
|
|
||||||
function UTF8ToString(utf8: Mono.Utf8Ptr): string;
|
|
||||||
var preloadPlugins: any[];
|
|
||||||
|
|
||||||
function stackSave(): Mono.StackSaveHandle;
|
|
||||||
function stackAlloc(length: number): number;
|
|
||||||
function stackRestore(handle: Mono.StackSaveHandle): void;
|
|
||||||
|
|
||||||
// These should probably be in @types/emscripten
|
|
||||||
function FS_createPath(parent, path, canRead, canWrite);
|
|
||||||
function FS_createDataFile(parent, name, data, canRead, canWrite, canOwn);
|
|
||||||
|
|
||||||
function mono_bind_static_method(fqn: string): BoundStaticMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare namespace Mono {
|
|
||||||
interface Utf8Ptr { Utf8Ptr__DO_NOT_IMPLEMENT: any }
|
|
||||||
interface StackSaveHandle { StackSaveHandle__DO_NOT_IMPLEMENT: any }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mono uses this global to hang various debugging-related items on
|
|
||||||
declare namespace MONO {
|
|
||||||
var loaded_files: string[];
|
|
||||||
var mono_wasm_runtime_is_ready: boolean;
|
|
||||||
function mono_wasm_runtime_ready (): void;
|
|
||||||
function mono_wasm_setenv (name: string, value: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mono_bind_static_method allows arbitrary JS data types to be sent over the wire. However we are
|
|
||||||
// artifically limiting it to a subset of types that we actually use.
|
|
||||||
declare type BoundStaticMethod = (...args: (string | number | null)[]) => (string | number | null);
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { Pointer, System_String } from '../Platform';
|
||||||
|
|
||||||
|
// Mono uses this global to hang various debugging-related items on
|
||||||
|
|
||||||
|
declare interface MONO {
|
||||||
|
loaded_files: string[];
|
||||||
|
mono_wasm_runtime_ready (): void;
|
||||||
|
mono_wasm_setenv (name: string, value: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mono uses this global to hold low-level interop APIs
|
||||||
|
declare interface BINDING {
|
||||||
|
js_string_to_mono_string(jsString: string): System_String;
|
||||||
|
js_typed_array_to_array(array: Uint8Array): Pointer;
|
||||||
|
js_typed_array_to_array<T>(array: Array<T>): Pointer;
|
||||||
|
conv_string(dotnetString: System_String | null): string | null;
|
||||||
|
bind_static_method(fqn: string, signature?: string): Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
var MONO: MONO;
|
||||||
|
var BINDING: BINDING;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { WebAssemblyResourceLoader } from "./WebAssemblyResourceLoader";
|
import { WebAssemblyResourceLoader } from './WebAssemblyResourceLoader';
|
||||||
|
|
||||||
export interface Platform {
|
export interface Platform {
|
||||||
start(resourceLoader: WebAssemblyResourceLoader): Promise<void>;
|
start(resourceLoader: WebAssemblyResourceLoader): Promise<void>;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { BootConfigResult } from './BootConfig';
|
||||||
|
import { System_String, Pointer } from './Platform';
|
||||||
|
|
||||||
|
export class WebAssemblyConfigLoader {
|
||||||
|
static async initAsync(bootConfigResult: BootConfigResult): Promise<void> {
|
||||||
|
window['Blazor']._internal.getApplicationEnvironment = () => BINDING.js_string_to_mono_string(bootConfigResult.applicationEnvironment);
|
||||||
|
|
||||||
|
const configFiles = await Promise.all((bootConfigResult.bootConfig.config || [])
|
||||||
|
.filter(name => name === 'appsettings.json' || name === `appsettings.${bootConfigResult.applicationEnvironment}.json`)
|
||||||
|
.map(async name => ({ name, content: await getConfigBytes(name) })));
|
||||||
|
|
||||||
|
window['Blazor']._internal.getConfig = (dotNetFileName: System_String) : Pointer | undefined => {
|
||||||
|
const fileName = BINDING.conv_string(dotNetFileName);
|
||||||
|
const resolvedFile = configFiles.find(f => f.name === fileName);
|
||||||
|
return resolvedFile ? BINDING.js_typed_array_to_array(resolvedFile.content) : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getConfigBytes(file: string): Promise<Uint8Array> {
|
||||||
|
const response = await fetch(file, {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
|
cache: 'no-cache'
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Uint8Array(await response.arrayBuffer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { toAbsoluteUri } from '../Services/NavigationManager';
|
import { toAbsoluteUri } from '../Services/NavigationManager';
|
||||||
|
import { BootJsonData, ResourceList } from './BootConfig';
|
||||||
const networkFetchCacheMode = 'no-cache';
|
const networkFetchCacheMode = 'no-cache';
|
||||||
|
|
||||||
export class WebAssemblyResourceLoader {
|
export class WebAssemblyResourceLoader {
|
||||||
|
|
@ -6,20 +7,12 @@ export class WebAssemblyResourceLoader {
|
||||||
private networkLoads: { [name: string]: LoadLogEntry } = {};
|
private networkLoads: { [name: string]: LoadLogEntry } = {};
|
||||||
private cacheLoads: { [name: string]: LoadLogEntry } = {};
|
private cacheLoads: { [name: string]: LoadLogEntry } = {};
|
||||||
|
|
||||||
static async initAsync(): Promise<WebAssemblyResourceLoader> {
|
static async initAsync(bootConfig: BootJsonData): Promise<WebAssemblyResourceLoader> {
|
||||||
const bootConfigResponse = await fetch('_framework/blazor.boot.json', {
|
|
||||||
method: 'GET',
|
|
||||||
credentials: 'include',
|
|
||||||
cache: networkFetchCacheMode
|
|
||||||
});
|
|
||||||
const bootConfig: BootJsonData = await bootConfigResponse.json();
|
|
||||||
const cache = await getCacheToUseIfEnabled(bootConfig);
|
const cache = await getCacheToUseIfEnabled(bootConfig);
|
||||||
|
|
||||||
return new WebAssemblyResourceLoader(bootConfig, cache);
|
return new WebAssemblyResourceLoader(bootConfig, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor (public readonly bootConfig: BootJsonData, private cacheIfUsed: Cache | null)
|
constructor(readonly bootConfig: BootJsonData, readonly cacheIfUsed: Cache | null) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadResources(resources: ResourceList, url: (name: string) => string): LoadingResource[] {
|
loadResources(resources: ResourceList, url: (name: string) => string): LoadingResource[] {
|
||||||
|
|
@ -36,7 +29,7 @@ export class WebAssemblyResourceLoader {
|
||||||
? this.loadResourceWithCaching(this.cacheIfUsed, name, url, contentHash)
|
? this.loadResourceWithCaching(this.cacheIfUsed, name, url, contentHash)
|
||||||
: fetch(url, { cache: networkFetchCacheMode, integrity: this.bootConfig.cacheBootResources ? contentHash : undefined });
|
: fetch(url, { cache: networkFetchCacheMode, integrity: this.bootConfig.cacheBootResources ? contentHash : undefined });
|
||||||
|
|
||||||
return { name, url, response };
|
return { name, url, response };
|
||||||
}
|
}
|
||||||
|
|
||||||
logToConsole() {
|
logToConsole() {
|
||||||
|
|
@ -166,7 +159,7 @@ function countTotalBytes(loads: LoadLogEntry[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toDataSizeString(byteCount: number) {
|
function toDataSizeString(byteCount: number) {
|
||||||
return `${(byteCount / (1024*1024)).toFixed(2)} MB`;
|
return `${(byteCount / (1024 * 1024)).toFixed(2)} MB`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPerformanceEntry(url: string): PerformanceResourceTiming | undefined {
|
function getPerformanceEntry(url: string): PerformanceResourceTiming | undefined {
|
||||||
|
|
@ -175,21 +168,6 @@ function getPerformanceEntry(url: string): PerformanceResourceTiming | undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep in sync with bootJsonData in Microsoft.AspNetCore.Blazor.Build
|
|
||||||
interface BootJsonData {
|
|
||||||
readonly entryAssembly: string;
|
|
||||||
readonly resources: ResourceGroups;
|
|
||||||
readonly debugBuild: boolean;
|
|
||||||
readonly linkerEnabled: boolean;
|
|
||||||
readonly cacheBootResources: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ResourceGroups {
|
|
||||||
readonly assembly: ResourceList;
|
|
||||||
readonly pdb?: ResourceList;
|
|
||||||
readonly runtime: ResourceList;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LoadLogEntry {
|
interface LoadLogEntry {
|
||||||
responseBytes: number | undefined;
|
responseBytes: number | undefined;
|
||||||
}
|
}
|
||||||
|
|
@ -199,5 +177,3 @@ export interface LoadingResource {
|
||||||
url: string;
|
url: string;
|
||||||
response: Promise<Response>;
|
response: Promise<Response>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResourceList = { [name: string]: string };
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { platform } from '../../Environment';
|
import { platform } from '../../Environment';
|
||||||
import { RenderBatch, ArrayRange, ArrayRangeReader, ArrayBuilderSegment, RenderTreeDiff, RenderTreeEdit, RenderTreeFrame, ArrayValues, EditType, FrameType, RenderTreeFrameReader } from './RenderBatch';
|
import { RenderBatch, ArrayRange, ArrayBuilderSegment, RenderTreeDiff, RenderTreeEdit, RenderTreeFrame, ArrayValues, EditType, FrameType, RenderTreeFrameReader } from './RenderBatch';
|
||||||
import { Pointer, System_Array, System_Object } from '../../Platform/Platform';
|
import { Pointer, System_Array, System_Object } from '../../Platform/Platform';
|
||||||
|
|
||||||
// Used when running on Mono WebAssembly for shared-memory interop. The code here encapsulates
|
// Used when running on Mono WebAssembly for shared-memory interop. The code here encapsulates
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
|
||||||
[Required]
|
[Required]
|
||||||
public bool CacheBootResources { get; set; }
|
public bool CacheBootResources { get; set; }
|
||||||
|
|
||||||
|
public ITaskItem[] ConfigurationFiles { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public string OutputPath { get; set; }
|
public string OutputPath { get; set; }
|
||||||
|
|
||||||
|
|
@ -59,7 +61,8 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
|
||||||
cacheBootResources = CacheBootResources,
|
cacheBootResources = CacheBootResources,
|
||||||
debugBuild = DebugBuild,
|
debugBuild = DebugBuild,
|
||||||
linkerEnabled = LinkerEnabled,
|
linkerEnabled = LinkerEnabled,
|
||||||
resources = new Dictionary<ResourceType, ResourceHashesByNameDictionary>()
|
resources = new Dictionary<ResourceType, ResourceHashesByNameDictionary>(),
|
||||||
|
config = new List<string>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build a two-level dictionary of the form:
|
// Build a two-level dictionary of the form:
|
||||||
|
|
@ -90,6 +93,14 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ConfigurationFiles != null)
|
||||||
|
{
|
||||||
|
foreach (var configFile in ConfigurationFiles)
|
||||||
|
{
|
||||||
|
result.config.Add(Path.GetFileName(configFile.ItemSpec));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var serializer = new DataContractJsonSerializer(typeof(BootJsonData), new DataContractJsonSerializerSettings
|
var serializer = new DataContractJsonSerializer(typeof(BootJsonData), new DataContractJsonSerializerSettings
|
||||||
{
|
{
|
||||||
UseSimpleDictionaryFormat = true
|
UseSimpleDictionaryFormat = true
|
||||||
|
|
@ -148,6 +159,11 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
|
||||||
/// Gets a value that determines if the linker is enabled.
|
/// Gets a value that determines if the linker is enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool linkerEnabled { get; set; }
|
public bool linkerEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Config files for the application
|
||||||
|
/// </summary>
|
||||||
|
public List<string> config { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ResourceType
|
public enum ResourceType
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@
|
||||||
$(ComponentsWebAssemblyFrameworkPath)" />
|
$(ComponentsWebAssemblyFrameworkPath)" />
|
||||||
|
|
||||||
<_WebAssemblyBCLAssembly Include="%(_WebAssemblyBCLFolder.Identity)*.dll" />
|
<_WebAssemblyBCLAssembly Include="%(_WebAssemblyBCLFolder.Identity)*.dll" />
|
||||||
|
|
||||||
|
<_BlazorConfigFile Include="wwwroot\appsettings*.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
@ -298,12 +300,43 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="_GenerateBlazorBootJsonInputHash">
|
||||||
|
<ItemGroup>
|
||||||
|
<_BlazorBootJsonHashInput Include="@(IntermediateAssembly)" />
|
||||||
|
<_BlazorBootJsonHashInput Include="@(_BlazorOutputWithTargetPath)" />
|
||||||
|
<_BlazorBootJsonHashInput Include="@(_BlazorConfigFile)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Hash ItemsToHash="@(_BlazorBootJsonHashInput)">
|
||||||
|
<Output TaskParameter="HashResult" PropertyName="_BlazorBootJsonInputHash" />
|
||||||
|
</Hash>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<_BlazorBootJsonInputHashFile>$(_BlazorIntermediateOutputPath)boot.json.input</_BlazorBootJsonInputHashFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<WriteLinesToFile
|
||||||
|
Lines="$(_BlazorBootJsonInputHash)"
|
||||||
|
File="$(_BlazorBootJsonInputHashFile)"
|
||||||
|
Overwrite="True"
|
||||||
|
WriteOnlyWhenDifferent="True" />
|
||||||
|
|
||||||
|
</Target>
|
||||||
|
|
||||||
<UsingTask TaskName="GenerateBlazorBootJson" AssemblyFile="$(_BlazorTasksPath)" />
|
<UsingTask TaskName="GenerateBlazorBootJson" AssemblyFile="$(_BlazorTasksPath)" />
|
||||||
|
|
||||||
<Target
|
<Target
|
||||||
Name="_GenerateBlazorBootJson"
|
Name="_GenerateBlazorBootJson"
|
||||||
Inputs="$(MSBuildAllProjects);@(_BlazorOutputWithTargetPath)"
|
DependsOnTargets="_GenerateBlazorBootJsonInputHash"
|
||||||
|
Inputs="$(MSBuildAllProjects);@(_BlazorOutputWithTargetPath);$(_BlazorBootJsonInputHashFile)"
|
||||||
Outputs="$(_BlazorBootJsonIntermediateOutputPath)">
|
Outputs="$(_BlazorBootJsonIntermediateOutputPath)">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<_IsDebugBuild>false</_IsDebugBuild>
|
||||||
|
<_IsDebugBuild Condition="'$(Configuration)' == 'Debug'">true</_IsDebugBuild>
|
||||||
|
<BlazorCacheBootResources Condition="'$(BlazorCacheBootResources)' == ''">true</BlazorCacheBootResources>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_BlazorBootResource Include="@(_BlazorOutputWithTargetPath->HasMetadata('BootManifestResourceType'))" />
|
<_BlazorBootResource Include="@(_BlazorOutputWithTargetPath->HasMetadata('BootManifestResourceType'))" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
@ -312,18 +345,14 @@
|
||||||
<Output TaskParameter="Items" ItemName="_BlazorBootResourceWithHash" />
|
<Output TaskParameter="Items" ItemName="_BlazorBootResourceWithHash" />
|
||||||
</GetFileHash>
|
</GetFileHash>
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<_IsDebugBuild>false</_IsDebugBuild>
|
|
||||||
<_IsDebugBuild Condition="'$(Configuration)' == 'Debug'">true</_IsDebugBuild>
|
|
||||||
<BlazorCacheBootResources Condition="'$(BlazorCacheBootResources)' == ''">true</BlazorCacheBootResources>
|
|
||||||
</PropertyGroup>
|
|
||||||
<GenerateBlazorBootJson
|
<GenerateBlazorBootJson
|
||||||
AssemblyPath="@(IntermediateAssembly)"
|
AssemblyPath="@(IntermediateAssembly)"
|
||||||
Resources="@(_BlazorBootResourceWithHash)"
|
Resources="@(_BlazorBootResourceWithHash)"
|
||||||
DebugBuild="$(_IsDebugBuild)"
|
DebugBuild="$(_IsDebugBuild)"
|
||||||
LinkerEnabled="$(BlazorWebAssemblyEnableLinking)"
|
LinkerEnabled="$(BlazorWebAssemblyEnableLinking)"
|
||||||
CacheBootResources="$(BlazorCacheBootResources)"
|
CacheBootResources="$(BlazorCacheBootResources)"
|
||||||
OutputPath="$(_BlazorBootJsonIntermediateOutputPath)" />
|
OutputPath="$(_BlazorBootJsonIntermediateOutputPath)"
|
||||||
|
ConfigurationFiles="@(_BlazorConfigFile)" />
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<_BlazorOutputWithTargetPath Include="$(_BlazorBootJsonIntermediateOutputPath)" TargetOutputPath="$(_BaseBlazorRuntimeOutputPath)$(_BlazorBootJsonName)" />
|
<_BlazorOutputWithTargetPath Include="$(_BlazorBootJsonIntermediateOutputPath)" TargetOutputPath="$(_BaseBlazorRuntimeOutputPath)$(_BlazorBootJsonName)" />
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,13 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build
|
||||||
"Microsoft.Bcl.AsyncInterfaces.dll",
|
"Microsoft.Bcl.AsyncInterfaces.dll",
|
||||||
"Microsoft.Extensions.Configuration.Abstractions.dll",
|
"Microsoft.Extensions.Configuration.Abstractions.dll",
|
||||||
"Microsoft.Extensions.Configuration.dll",
|
"Microsoft.Extensions.Configuration.dll",
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions.dll",
|
||||||
|
"Microsoft.Extensions.Configuration.Json.dll",
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions.dll",
|
"Microsoft.Extensions.DependencyInjection.Abstractions.dll",
|
||||||
"Microsoft.Extensions.DependencyInjection.dll",
|
"Microsoft.Extensions.DependencyInjection.dll",
|
||||||
|
"Microsoft.Extensions.FileProviders.Abstractions.dll",
|
||||||
|
"Microsoft.Extensions.FileProviders.Physical.dll",
|
||||||
|
"Microsoft.Extensions.FileSystemGlobbing.dll",
|
||||||
"Microsoft.Extensions.Logging.Abstractions.dll",
|
"Microsoft.Extensions.Logging.Abstractions.dll",
|
||||||
"Microsoft.Extensions.Primitives.dll",
|
"Microsoft.Extensions.Primitives.dll",
|
||||||
"Microsoft.JSInterop.dll",
|
"Microsoft.JSInterop.dll",
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ using System.Linq;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Components.WebAssembly.DevServer.Server
|
namespace Microsoft.AspNetCore.Components.WebAssembly.DevServer.Server
|
||||||
{
|
{
|
||||||
|
|
@ -33,6 +32,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.DevServer.Server
|
||||||
|
|
||||||
var inMemoryConfiguration = new Dictionary<string, string>
|
var inMemoryConfiguration = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
|
[WebHostDefaults.EnvironmentKey] = "Development",
|
||||||
["Logging:LogLevel:Microsoft"] = "Warning",
|
["Logging:LogLevel:Microsoft"] = "Warning",
|
||||||
["Logging:LogLevel:Microsoft.Hosting.Lifetime"] = "Information",
|
["Logging:LogLevel:Microsoft.Hosting.Lifetime"] = "Information",
|
||||||
[WebHostDefaults.StaticWebAssetsKey] = name,
|
[WebHostDefaults.StaticWebAssetsKey] = name,
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,12 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
!rest.StartsWithSegments("/_framework/blazor.server.js"),
|
!rest.StartsWithSegments("/_framework/blazor.server.js"),
|
||||||
subBuilder =>
|
subBuilder =>
|
||||||
{
|
{
|
||||||
subBuilder.Use(async (ctx, next) =>
|
subBuilder.Use(async (context, next) =>
|
||||||
{
|
{
|
||||||
|
context.Response.Headers.Append("Blazor-Environment", webHostEnvironment.EnvironmentName);
|
||||||
|
|
||||||
// This will invoke the static files middleware plugged-in below.
|
// This will invoke the static files middleware plugged-in below.
|
||||||
NegotiateEncoding(ctx, webHostEnvironment);
|
NegotiateEncoding(context, webHostEnvironment);
|
||||||
await next();
|
await next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
// 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 System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides information about the hosting environment an application is running in.
|
||||||
|
/// </summary>
|
||||||
|
public interface IWebAssemblyHostEnvironment
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the environment. This is configured to use the environment of the application hosting the Blazor WebAssembly application.
|
||||||
|
/// Configured to "Production" when not specified by the host.
|
||||||
|
/// </summary>
|
||||||
|
string Environment { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,13 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using Microsoft.AspNetCore.Components.Routing;
|
using Microsoft.AspNetCore.Components.Routing;
|
||||||
using Microsoft.AspNetCore.Components.WebAssembly.Services;
|
using Microsoft.AspNetCore.Components.WebAssembly.Services;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Configuration.Json;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -59,6 +62,41 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
||||||
{
|
{
|
||||||
return Services.BuildServiceProvider();
|
return Services.BuildServiceProvider();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
InitializeEnvironment();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeEnvironment()
|
||||||
|
{
|
||||||
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Create("WEBASSEMBLY")))
|
||||||
|
{
|
||||||
|
// The remainder of this method relies on the ability to make .NET WebAssembly-specific JSInterop calls.
|
||||||
|
// Note that this short-circuit exists as a way for unit tests running in .NET Core without JSInterop to run.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var applicationEnvironment = DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled<string>("Blazor._internal.getApplicationEnvironment");
|
||||||
|
Services.AddSingleton<IWebAssemblyHostEnvironment>(new WebAssemblyHostEnvironment(applicationEnvironment));
|
||||||
|
|
||||||
|
var configFiles = new[]
|
||||||
|
{
|
||||||
|
"appsettings.json",
|
||||||
|
$"appsettings.{applicationEnvironment}.json"
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var configFile in configFiles)
|
||||||
|
{
|
||||||
|
var appSettingsJson = DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled<string, byte[]>(
|
||||||
|
"Blazor._internal.getConfig",
|
||||||
|
configFile);
|
||||||
|
|
||||||
|
if (appSettingsJson != null)
|
||||||
|
{
|
||||||
|
// Perf: Using this over AddJsonStream. This allows the linker to trim out the "File"-specific APIs and assemblies
|
||||||
|
// for Configuration, of where there are several.
|
||||||
|
Configuration.Add<JsonStreamConfigurationSource>(s => s.Stream = new MemoryStream(appSettingsJson));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -130,7 +168,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
||||||
return new WebAssemblyHost(services, scope, configuration, RootComponents.ToArray());
|
return new WebAssemblyHost(services, scope, configuration, RootComponents.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeDefaultServices()
|
internal void InitializeDefaultServices()
|
||||||
{
|
{
|
||||||
Services.AddSingleton<IJSRuntime>(DefaultWebAssemblyJSRuntime.Instance);
|
Services.AddSingleton<IJSRuntime>(DefaultWebAssemblyJSRuntime.Instance);
|
||||||
Services.AddSingleton<NavigationManager>(WebAssemblyNavigationManager.Instance);
|
Services.AddSingleton<NavigationManager>(WebAssemblyNavigationManager.Instance);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting
|
||||||
|
{
|
||||||
|
internal sealed class WebAssemblyHostEnvironment : IWebAssemblyHostEnvironment
|
||||||
|
{
|
||||||
|
public WebAssemblyHostEnvironment(string environment) => Environment = environment;
|
||||||
|
|
||||||
|
public string Environment { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Components.Web" />
|
<Reference Include="Microsoft.AspNetCore.Components.Web" />
|
||||||
<Reference Include="Microsoft.Extensions.Configuration" />
|
<Reference Include="Microsoft.Extensions.Configuration.Json" />
|
||||||
<Reference Include="Microsoft.JSInterop.WebAssembly" />
|
<Reference Include="Microsoft.JSInterop.WebAssembly" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using BasicTestApp;
|
||||||
|
using Microsoft.AspNetCore.Components.E2ETest;
|
||||||
|
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
|
||||||
|
using Microsoft.AspNetCore.E2ETesting;
|
||||||
|
using OpenQA.Selenium;
|
||||||
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
||||||
|
{
|
||||||
|
public class WebAssemblyConfigurationHostedTest : ServerTestBase<BasicTestAppServerSiteFixture<TestServer.ClientStartup>>
|
||||||
|
{
|
||||||
|
private IWebElement _appElement;
|
||||||
|
|
||||||
|
public WebAssemblyConfigurationHostedTest(
|
||||||
|
BrowserFixture browserFixture,
|
||||||
|
BasicTestAppServerSiteFixture<TestServer.ClientStartup> serverFixture,
|
||||||
|
ITestOutputHelper output) :
|
||||||
|
base(browserFixture, serverFixture, output)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InitializeAsyncCore()
|
||||||
|
{
|
||||||
|
base.InitializeAsyncCore();
|
||||||
|
|
||||||
|
Navigate(ServerPathBase, noReload: false);
|
||||||
|
_appElement = Browser.MountTestComponent<ConfigurationComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WebAssemblyConfiguration_Works()
|
||||||
|
{
|
||||||
|
// Verify values from the default 'appsettings.json' are read.
|
||||||
|
Browser.Equal("Default key1-value", () => _appElement.FindElement(By.Id("key1")).Text);
|
||||||
|
|
||||||
|
// Verify values overriden by an environment specific 'appsettings.$(Environment).json are read
|
||||||
|
Assert.Equal("Prod key2-value", _appElement.FindElement(By.Id("key2")).Text);
|
||||||
|
|
||||||
|
// Lastly for sanity, make sure values specified in an environment specific 'appsettings.$(Environment).json are read
|
||||||
|
Assert.Equal("Prod key3-value", _appElement.FindElement(By.Id("key3")).Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WebAssemblyHostingEnvironment_Works()
|
||||||
|
{
|
||||||
|
// Verify values from the default 'appsettings.json' are read.
|
||||||
|
Browser.Equal("Production", () => _appElement.FindElement(By.Id("environment")).Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using BasicTestApp;
|
||||||
|
using Microsoft.AspNetCore.Components.E2ETest;
|
||||||
|
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
|
||||||
|
using Microsoft.AspNetCore.E2ETesting;
|
||||||
|
using OpenQA.Selenium;
|
||||||
|
using TestServer;
|
||||||
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
||||||
|
{
|
||||||
|
public class WebAssemblyConfigurationTest : ServerTestBase<DevHostServerFixture<BasicTestApp.Program>>
|
||||||
|
{
|
||||||
|
private IWebElement _appElement;
|
||||||
|
|
||||||
|
public WebAssemblyConfigurationTest(
|
||||||
|
BrowserFixture browserFixture,
|
||||||
|
DevHostServerFixture<BasicTestApp.Program> serverFixture,
|
||||||
|
ITestOutputHelper output) :
|
||||||
|
base(browserFixture, serverFixture, output)
|
||||||
|
{
|
||||||
|
_serverFixture.PathBase = "/subdir";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InitializeAsyncCore()
|
||||||
|
{
|
||||||
|
base.InitializeAsyncCore();
|
||||||
|
|
||||||
|
Navigate(ServerPathBase, noReload: false);
|
||||||
|
_appElement = Browser.MountTestComponent<ConfigurationComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WebAssemblyConfiguration_Works()
|
||||||
|
{
|
||||||
|
// Verify values from the default 'appsettings.json' are read.
|
||||||
|
Browser.Equal("Default key1-value", () => _appElement.FindElement(By.Id("key1")).Text);
|
||||||
|
|
||||||
|
// Verify values overriden by an environment specific 'appsettings.$(Environment).json are read
|
||||||
|
Assert.Equal("Development key2-value", _appElement.FindElement(By.Id("key2")).Text);
|
||||||
|
|
||||||
|
// Lastly for sanity, make sure values specified in an environment specific 'appsettings.$(Environment).json are read
|
||||||
|
Assert.Equal("Development key3-value", _appElement.FindElement(By.Id("key3")).Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WebAssemblyHostingEnvironment_Works()
|
||||||
|
{
|
||||||
|
// Dev-Server defaults to Development. It's in the name!
|
||||||
|
Browser.Equal("Development", () => _appElement.FindElement(By.Id("environment")).Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
@inject Microsoft.Extensions.Configuration.IConfiguration Config
|
||||||
|
@inject Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment HostEnvironment
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li id="key1">@Config["key1"]</li>
|
||||||
|
<li id="key2">@Config["key2"]</li>
|
||||||
|
<li id="key3">@Config["key3"]</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div id="environment">@HostEnvironment.Environment</div>
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
<option value="BasicTestApp.CascadingValueTest.CascadingValueSupplier">Cascading values</option>
|
<option value="BasicTestApp.CascadingValueTest.CascadingValueSupplier">Cascading values</option>
|
||||||
<option value="BasicTestApp.ComponentRefComponent">Component ref component</option>
|
<option value="BasicTestApp.ComponentRefComponent">Component ref component</option>
|
||||||
<option value="BasicTestApp.ConcurrentRenderParent">Concurrent rendering</option>
|
<option value="BasicTestApp.ConcurrentRenderParent">Concurrent rendering</option>
|
||||||
|
<option value="BasicTestApp.ConfigurationComponent">Configuration</option>
|
||||||
<option value="BasicTestApp.CounterComponent">Counter</option>
|
<option value="BasicTestApp.CounterComponent">Counter</option>
|
||||||
<option value="BasicTestApp.CounterComponentUsingChild">Counter using child component</option>
|
<option value="BasicTestApp.CounterComponentUsingChild">Counter using child component</option>
|
||||||
<option value="BasicTestApp.CounterComponentWrapper">Counter wrapped in parent</option>
|
<option value="BasicTestApp.CounterComponentWrapper">Counter wrapped in parent</option>
|
||||||
|
|
@ -28,12 +29,12 @@
|
||||||
<option value="BasicTestApp.EventPreventDefaultComponent">Event preventDefault</option>
|
<option value="BasicTestApp.EventPreventDefaultComponent">Event preventDefault</option>
|
||||||
<option value="BasicTestApp.ExternalContentPackage">External content package</option>
|
<option value="BasicTestApp.ExternalContentPackage">External content package</option>
|
||||||
<option value="BasicTestApp.FocusEventComponent">Focus events</option>
|
<option value="BasicTestApp.FocusEventComponent">Focus events</option>
|
||||||
|
<option value="BasicTestApp.FormsTest.ExperimentalValidationComponent">Experimental validation</option>
|
||||||
<option value="BasicTestApp.FormsTest.NotifyPropertyChangedValidationComponent">INotifyPropertyChanged validation</option>
|
<option value="BasicTestApp.FormsTest.NotifyPropertyChangedValidationComponent">INotifyPropertyChanged validation</option>
|
||||||
<option value="BasicTestApp.FormsTest.SimpleValidationComponent">Simple validation</option>
|
<option value="BasicTestApp.FormsTest.SimpleValidationComponent">Simple validation</option>
|
||||||
<option value="BasicTestApp.FormsTest.SimpleValidationComponentUsingExperimentalValidator">Simple validation using experimental validator</option>
|
<option value="BasicTestApp.FormsTest.SimpleValidationComponentUsingExperimentalValidator">Simple validation using experimental validator</option>
|
||||||
<option value="BasicTestApp.FormsTest.TypicalValidationComponent">Typical validation</option>
|
<option value="BasicTestApp.FormsTest.TypicalValidationComponent">Typical validation</option>
|
||||||
<option value="BasicTestApp.FormsTest.TypicalValidationComponentUsingExperimentalValidator">Typical validation using experimental validator</option>
|
<option value="BasicTestApp.FormsTest.TypicalValidationComponentUsingExperimentalValidator">Typical validation using experimental validator</option>
|
||||||
<option value="BasicTestApp.FormsTest.ExperimentalValidationComponent">Experimental validation</option>
|
|
||||||
<option value="BasicTestApp.GlobalizationBindCases">Globalization Bind Cases</option>
|
<option value="BasicTestApp.GlobalizationBindCases">Globalization Bind Cases</option>
|
||||||
<option value="BasicTestApp.HierarchicalImportsTest.Subdir.ComponentUsingImports">Imports statement</option>
|
<option value="BasicTestApp.HierarchicalImportsTest.Subdir.ComponentUsingImports">Imports statement</option>
|
||||||
<option value="BasicTestApp.HtmlBlockChildContent">ChildContent HTML Block</option>
|
<option value="BasicTestApp.HtmlBlockChildContent">ChildContent HTML Block</option>
|
||||||
|
|
@ -60,10 +61,10 @@
|
||||||
<option value="BasicTestApp.ParentChildComponent">Parent component with child</option>
|
<option value="BasicTestApp.ParentChildComponent">Parent component with child</option>
|
||||||
<option value="BasicTestApp.PropertiesChangedHandlerParent">Parent component that changes parameters on child</option>
|
<option value="BasicTestApp.PropertiesChangedHandlerParent">Parent component that changes parameters on child</option>
|
||||||
<option value="BasicTestApp.RazorTemplates">Razor Templates</option>
|
<option value="BasicTestApp.RazorTemplates">Razor Templates</option>
|
||||||
|
<option value="BasicTestApp.Reconnection.ReconnectionComponent">Reconnection server-side blazor</option>
|
||||||
<option value="BasicTestApp.RedTextComponent">Red text</option>
|
<option value="BasicTestApp.RedTextComponent">Red text</option>
|
||||||
<option value="BasicTestApp.ReliabilityComponent">Server reliability component</option>
|
<option value="BasicTestApp.ReliabilityComponent">Server reliability component</option>
|
||||||
<option value="BasicTestApp.RenderFragmentToggler">Render fragment renderer</option>
|
<option value="BasicTestApp.RenderFragmentToggler">Render fragment renderer</option>
|
||||||
<option value="BasicTestApp.Reconnection.ReconnectionComponent">Reconnection server-side blazor</option>
|
|
||||||
<option value="BasicTestApp.ReorderingFocusComponent">Reordering focus retention</option>
|
<option value="BasicTestApp.ReorderingFocusComponent">Reordering focus retention</option>
|
||||||
<option value="BasicTestApp.RouterTest.NavigationManagerComponent">NavigationManager Test</option>
|
<option value="BasicTestApp.RouterTest.NavigationManagerComponent">NavigationManager Test</option>
|
||||||
<option value="BasicTestApp.RouterTest.TestRouter">Router</option>
|
<option value="BasicTestApp.RouterTest.TestRouter">Router</option>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"key2": "Development key2-value",
|
||||||
|
"key3": "Development key3-value"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"key2": "Prod key2-value",
|
||||||
|
"key3": "Prod key3-value"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"key1": "Default key1-value",
|
||||||
|
"key2": "Default key2-value"
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue