Use System.Reflection.Metadata to generate BootConfig (#17156)
* Use System.Reflection.Metadata to generate BootConfig * Remove reference to Mono.Cecil * Remove support for auto embedded css \ js * Remove blazor.webassembly.js
This commit is contained in:
parent
92fce0da09
commit
5bdf75f3e1
|
|
@ -15,10 +15,6 @@ namespace Microsoft.AspNetCore.Blazor.Build.DevServer.Commands
|
|||
"The path to a file that lists the paths to given referenced dll files",
|
||||
CommandOptionType.SingleValue);
|
||||
|
||||
var embeddedResourcesFile = command.Option("--embedded-resources",
|
||||
"The path to a file that lists the paths of .NET assemblies that may contain embedded resources (typically, referenced assemblies in their pre-linked states)",
|
||||
CommandOptionType.SingleValue);
|
||||
|
||||
var outputPath = command.Option("--output",
|
||||
"Path to the output file",
|
||||
CommandOptionType.SingleValue);
|
||||
|
|
@ -44,14 +40,9 @@ namespace Microsoft.AspNetCore.Blazor.Build.DevServer.Commands
|
|||
? File.ReadAllLines(referencesFile.Value())
|
||||
: Array.Empty<string>();
|
||||
|
||||
var embeddedResourcesSources = embeddedResourcesFile.HasValue()
|
||||
? File.ReadAllLines(embeddedResourcesFile.Value())
|
||||
: Array.Empty<string>();
|
||||
|
||||
BootJsonWriter.WriteFile(
|
||||
mainAssemblyPath.Value,
|
||||
referencesSources,
|
||||
embeddedResourcesSources,
|
||||
linkerEnabledFlag.HasValue(),
|
||||
outputPath.Value());
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build
|
||||
{
|
||||
|
|
@ -16,79 +15,55 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
public static void WriteFile(
|
||||
string assemblyPath,
|
||||
string[] assemblyReferences,
|
||||
string[] embeddedResourcesSources,
|
||||
bool linkerEnabled,
|
||||
string outputPath)
|
||||
{
|
||||
var embeddedContent = EmbeddedResourcesProcessor.ExtractEmbeddedResources(
|
||||
embeddedResourcesSources, Path.GetDirectoryName(outputPath));
|
||||
var bootJsonText = GetBootJsonContent(
|
||||
Path.GetFileName(assemblyPath),
|
||||
GetAssemblyEntryPoint(assemblyPath),
|
||||
AssemblyName.GetAssemblyName(assemblyPath).Name,
|
||||
assemblyReferences,
|
||||
embeddedContent,
|
||||
linkerEnabled);
|
||||
var normalizedOutputPath = Path.GetFullPath(outputPath);
|
||||
Console.WriteLine("Writing boot data to: " + normalizedOutputPath);
|
||||
File.WriteAllText(normalizedOutputPath, bootJsonText);
|
||||
}
|
||||
|
||||
public static string GetBootJsonContent(string assemblyFileName, string entryPoint, string[] assemblyReferences, IEnumerable<EmbeddedResourceInfo> embeddedContent, bool linkerEnabled)
|
||||
public static string GetBootJsonContent(string entryAssembly, string[] assemblyReferences, bool linkerEnabled)
|
||||
{
|
||||
var data = new BootJsonData(
|
||||
assemblyFileName,
|
||||
entryPoint,
|
||||
entryAssembly,
|
||||
assemblyReferences,
|
||||
embeddedContent,
|
||||
linkerEnabled);
|
||||
return JsonSerializer.Serialize(data, JsonSerializerOptionsProvider.Options);
|
||||
}
|
||||
|
||||
private static string GetAssemblyEntryPoint(string assemblyPath)
|
||||
{
|
||||
using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath))
|
||||
{
|
||||
var entryPoint = assemblyDefinition.EntryPoint;
|
||||
if (entryPoint == null)
|
||||
{
|
||||
throw new ArgumentException($"The assembly at {assemblyPath} has no specified entry point.");
|
||||
}
|
||||
|
||||
return $"{entryPoint.DeclaringType.FullName}::{entryPoint.Name}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the structure of a Blazor boot JSON file
|
||||
/// </summary>
|
||||
class BootJsonData
|
||||
readonly struct BootJsonData
|
||||
{
|
||||
public string Main { get; }
|
||||
public string EntryPoint { get; }
|
||||
public IEnumerable<string> AssemblyReferences { get; }
|
||||
public IEnumerable<string> CssReferences { get; }
|
||||
public IEnumerable<string> JsReferences { get; }
|
||||
/// <summary>
|
||||
/// Gets the name of the assembly with the application entry point
|
||||
/// </summary>
|
||||
public string EntryAssembly { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the closure of assemblies to be loaded by Blazor WASM. This includes the application entry assembly.
|
||||
/// </summary>
|
||||
public IEnumerable<string> Assemblies { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value that determines if the linker is enabled.
|
||||
/// </summary>
|
||||
public bool LinkerEnabled { get; }
|
||||
|
||||
public BootJsonData(
|
||||
string entrypointAssemblyWithExtension,
|
||||
string entryPoint,
|
||||
IEnumerable<string> assemblyReferences,
|
||||
IEnumerable<EmbeddedResourceInfo> embeddedContent,
|
||||
string entryAssembly,
|
||||
IEnumerable<string> assemblies,
|
||||
bool linkerEnabled)
|
||||
{
|
||||
Main = entrypointAssemblyWithExtension;
|
||||
EntryPoint = entryPoint;
|
||||
AssemblyReferences = assemblyReferences;
|
||||
EntryAssembly = entryAssembly;
|
||||
Assemblies = assemblies;
|
||||
LinkerEnabled = linkerEnabled;
|
||||
|
||||
CssReferences = embeddedContent
|
||||
.Where(c => c.Kind == EmbeddedResourceKind.Css)
|
||||
.Select(c => c.RelativePath);
|
||||
|
||||
JsReferences = embeddedContent
|
||||
.Where(c => c.Kind == EmbeddedResourceKind.JavaScript)
|
||||
.Select(c => c.RelativePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
// 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.Blazor.Build
|
||||
{
|
||||
internal class EmbeddedResourceInfo
|
||||
{
|
||||
public EmbeddedResourceKind Kind { get; }
|
||||
public string RelativePath { get; }
|
||||
|
||||
public EmbeddedResourceInfo(EmbeddedResourceKind kind, string relativePath)
|
||||
{
|
||||
Kind = kind;
|
||||
RelativePath = relativePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
// 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.Blazor.Build
|
||||
{
|
||||
internal enum EmbeddedResourceKind
|
||||
{
|
||||
JavaScript,
|
||||
Css,
|
||||
Static
|
||||
}
|
||||
}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
// 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 Mono.Cecil;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build
|
||||
{
|
||||
internal class EmbeddedResourcesProcessor
|
||||
{
|
||||
const string ContentSubdirName = "_content";
|
||||
|
||||
private readonly static Dictionary<string, EmbeddedResourceKind> _knownResourceKindsByNamePrefix = new Dictionary<string, EmbeddedResourceKind>
|
||||
{
|
||||
{ "blazor:js:", EmbeddedResourceKind.JavaScript },
|
||||
{ "blazor:css:", EmbeddedResourceKind.Css },
|
||||
{ "blazor:file:", EmbeddedResourceKind.Static },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Finds Blazor-specific embedded resources in the specified assemblies, writes them
|
||||
/// to disk, and returns a description of those resources in dependency order.
|
||||
/// </summary>
|
||||
/// <param name="referencedAssemblyPaths">The paths to assemblies that may contain embedded resources.</param>
|
||||
/// <param name="outputDir">The path to the directory where output is being written.</param>
|
||||
/// <returns>A description of the embedded resources that were written to disk.</returns>
|
||||
public static IReadOnlyList<EmbeddedResourceInfo> ExtractEmbeddedResources(
|
||||
IEnumerable<string> referencedAssemblyPaths, string outputDir)
|
||||
{
|
||||
// Clean away any earlier state
|
||||
var contentDir = Path.Combine(outputDir, ContentSubdirName);
|
||||
if (Directory.Exists(contentDir))
|
||||
{
|
||||
Directory.Delete(contentDir, recursive: true);
|
||||
}
|
||||
|
||||
// First, get an ordered list of AssemblyDefinition instances
|
||||
var referencedAssemblyDefinitions = referencedAssemblyPaths
|
||||
.Where(path => !Path.GetFileName(path).StartsWith("System.", StringComparison.Ordinal)) // Skip System.* because they are never going to contain embedded resources that we want
|
||||
.Select(path => AssemblyDefinition.ReadAssembly(path))
|
||||
.ToList();
|
||||
referencedAssemblyDefinitions.Sort(OrderWithReferenceSubjectFirst);
|
||||
|
||||
// Now process them in turn
|
||||
return referencedAssemblyDefinitions
|
||||
.SelectMany(def => ExtractEmbeddedResourcesFromSingleAssembly(def, outputDir))
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
}
|
||||
|
||||
private static IEnumerable<EmbeddedResourceInfo> ExtractEmbeddedResourcesFromSingleAssembly(
|
||||
AssemblyDefinition assemblyDefinition, string outputDirPath)
|
||||
{
|
||||
var assemblyName = assemblyDefinition.Name.Name;
|
||||
foreach (var res in assemblyDefinition.MainModule.Resources)
|
||||
{
|
||||
if (TryExtractEmbeddedResource(assemblyName, res, outputDirPath, out var extractedResourceInfo))
|
||||
{
|
||||
yield return extractedResourceInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryExtractEmbeddedResource(string assemblyName, Resource resource, string outputDirPath, out EmbeddedResourceInfo extractedResourceInfo)
|
||||
{
|
||||
if (resource is EmbeddedResource embeddedResource)
|
||||
{
|
||||
if (TryInterpretLogicalName(resource.Name, out var kind, out var name))
|
||||
{
|
||||
// Prefix the output path with the assembly name to ensure no clashes
|
||||
// Also be invariant to the OS on which the package was built
|
||||
name = Path.Combine(ContentSubdirName, assemblyName, EnsureHasPathSeparators(name, Path.DirectorySeparatorChar));
|
||||
|
||||
// Write the file content to disk, ensuring we don't try to write outside the output root
|
||||
var outputPath = Path.GetFullPath(Path.Combine(outputDirPath, name));
|
||||
if (!outputPath.StartsWith(outputDirPath))
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot write embedded resource from assembly '{assemblyName}' to '{outputPath}' because it is outside the expected directory {outputDirPath}");
|
||||
}
|
||||
WriteResourceFile(embeddedResource, outputPath);
|
||||
|
||||
// The URLs we write into the boot json file need to use web-style directory separators
|
||||
extractedResourceInfo = new EmbeddedResourceInfo(kind, EnsureHasPathSeparators(name, '/'));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extractedResourceInfo = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void WriteResourceFile(EmbeddedResource resource, string outputPath)
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
|
||||
using (var outputStream = File.OpenWrite(outputPath))
|
||||
{
|
||||
resource.GetResourceStream().CopyTo(outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
private static string EnsureHasPathSeparators(string name, char desiredSeparatorChar) => name
|
||||
.Replace('\\', desiredSeparatorChar)
|
||||
.Replace('/', desiredSeparatorChar);
|
||||
|
||||
private static bool TryInterpretLogicalName(string logicalName, out EmbeddedResourceKind kind, out string resolvedName)
|
||||
{
|
||||
foreach (var kvp in _knownResourceKindsByNamePrefix)
|
||||
{
|
||||
if (logicalName.StartsWith(kvp.Key, StringComparison.Ordinal))
|
||||
{
|
||||
kind = kvp.Value;
|
||||
resolvedName = logicalName.Substring(kvp.Key.Length);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
kind = default;
|
||||
resolvedName = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
// For each assembly B that references A, we want the resources from A to be loaded before
|
||||
// the references for B (because B's resources might depend on A's resources)
|
||||
private static int OrderWithReferenceSubjectFirst(AssemblyDefinition a, AssemblyDefinition b)
|
||||
=> AssemblyHasReference(a, b) ? 1
|
||||
: AssemblyHasReference(b, a) ? -1
|
||||
: 0;
|
||||
|
||||
private static bool AssemblyHasReference(AssemblyDefinition from, AssemblyDefinition to)
|
||||
=> from.MainModule.AssemblyReferences
|
||||
.Select(reference => reference.Name)
|
||||
.Contains(to.Name.Name, StringComparer.Ordinal);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,9 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Mono.Cecil;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build
|
||||
{
|
||||
|
|
@ -27,19 +29,15 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
string[] applicationDependencies,
|
||||
string[] monoBclDirectories)
|
||||
{
|
||||
var assembly = new AssemblyEntry(entryPoint, AssemblyDefinition.ReadAssembly(entryPoint));
|
||||
var entryAssembly = new AssemblyEntry(entryPoint, GetAssemblyName(entryPoint));
|
||||
|
||||
var dependencies = applicationDependencies
|
||||
.Select(a => new AssemblyEntry(a, AssemblyDefinition.ReadAssembly(a)))
|
||||
.ToArray();
|
||||
var dependencies = CreateAssemblyLookup(applicationDependencies);
|
||||
|
||||
var bcl = monoBclDirectories
|
||||
.SelectMany(d => Directory.EnumerateFiles(d, "*.dll").Select(f => Path.Combine(d, f)))
|
||||
.Select(a => new AssemblyEntry(a, AssemblyDefinition.ReadAssembly(a)))
|
||||
.ToArray();
|
||||
var bcl = CreateAssemblyLookup(monoBclDirectories
|
||||
.SelectMany(d => Directory.EnumerateFiles(d, "*.dll").Select(f => Path.Combine(d, f))));
|
||||
|
||||
var assemblyResolutionContext = new AssemblyResolutionContext(
|
||||
assembly,
|
||||
entryAssembly,
|
||||
dependencies,
|
||||
bcl);
|
||||
|
||||
|
|
@ -47,6 +45,28 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
|
||||
var paths = assemblyResolutionContext.Results.Select(r => r.Path);
|
||||
return paths.Concat(FindPdbs(paths));
|
||||
|
||||
static Dictionary<string, AssemblyEntry> CreateAssemblyLookup(IEnumerable<string> assemblyPaths)
|
||||
{
|
||||
var dictionary = new Dictionary<string, AssemblyEntry>(StringComparer.Ordinal);
|
||||
foreach (var path in assemblyPaths)
|
||||
{
|
||||
var assemblyName = AssemblyName.GetAssemblyName(path).Name;
|
||||
if (dictionary.TryGetValue(assemblyName, out var previous))
|
||||
{
|
||||
throw new InvalidOperationException($"Multiple assemblies found with the same assembly name '{assemblyName}':" +
|
||||
Environment.NewLine + string.Join(Environment.NewLine, previous, path));
|
||||
}
|
||||
dictionary[assemblyName] = new AssemblyEntry(path, assemblyName);
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetAssemblyName(string assemblyPath)
|
||||
{
|
||||
return AssemblyName.GetAssemblyName(assemblyPath).Name;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> FindPdbs(IEnumerable<string> dllPaths)
|
||||
|
|
@ -59,46 +79,40 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
public class AssemblyResolutionContext
|
||||
{
|
||||
public AssemblyResolutionContext(
|
||||
AssemblyEntry assembly,
|
||||
AssemblyEntry[] dependencies,
|
||||
AssemblyEntry[] bcl)
|
||||
AssemblyEntry entryAssembly,
|
||||
Dictionary<string, AssemblyEntry> dependencies,
|
||||
Dictionary<string, AssemblyEntry> bcl)
|
||||
{
|
||||
Assembly = assembly;
|
||||
EntryAssembly = entryAssembly;
|
||||
Dependencies = dependencies;
|
||||
Bcl = bcl;
|
||||
}
|
||||
|
||||
public AssemblyEntry Assembly { get; }
|
||||
public AssemblyEntry[] Dependencies { get; }
|
||||
public AssemblyEntry[] Bcl { get; }
|
||||
public AssemblyEntry EntryAssembly { get; }
|
||||
public Dictionary<string, AssemblyEntry> Dependencies { get; }
|
||||
public Dictionary<string, AssemblyEntry> Bcl { get; }
|
||||
|
||||
public IList<AssemblyEntry> Results { get; } = new List<AssemblyEntry>();
|
||||
|
||||
internal void ResolveAssemblies()
|
||||
{
|
||||
var visitedAssemblies = new HashSet<string>();
|
||||
var pendingAssemblies = new Stack<AssemblyNameReference>();
|
||||
pendingAssemblies.Push(Assembly.Definition.Name);
|
||||
var pendingAssemblies = new Stack<string>();
|
||||
pendingAssemblies.Push(EntryAssembly.Name);
|
||||
ResolveAssembliesCore();
|
||||
|
||||
void ResolveAssembliesCore()
|
||||
{
|
||||
while (pendingAssemblies.TryPop(out var current))
|
||||
{
|
||||
if (!visitedAssemblies.Contains(current.Name))
|
||||
if (visitedAssemblies.Add(current))
|
||||
{
|
||||
visitedAssemblies.Add(current.Name);
|
||||
|
||||
// Not all references will be resolvable within the Mono BCL, particularly
|
||||
// when building for server-side Blazor as you will be running on CoreCLR
|
||||
// and therefore may depend on System.* BCL assemblies that aren't present
|
||||
// in Mono WebAssembly. Skipping unresolved assemblies here is equivalent
|
||||
// to passing "--skip-unresolved true" to the Mono linker.
|
||||
var resolved = Resolve(current);
|
||||
if (resolved != null)
|
||||
// Not all references will be resolvable within the Mono BCL.
|
||||
// Skipping unresolved assemblies here is equivalent to passing "--skip-unresolved true" to the Mono linker.
|
||||
if (Resolve(current) is AssemblyEntry resolved)
|
||||
{
|
||||
Results.Add(resolved);
|
||||
var references = GetAssemblyReferences(resolved);
|
||||
var references = GetAssemblyReferences(resolved.Path);
|
||||
foreach (var reference in references)
|
||||
{
|
||||
pendingAssemblies.Push(reference);
|
||||
|
|
@ -108,58 +122,70 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
}
|
||||
}
|
||||
|
||||
IEnumerable<AssemblyNameReference> GetAssemblyReferences(AssemblyEntry current) =>
|
||||
current.Definition.Modules.SelectMany(m => m.AssemblyReferences);
|
||||
|
||||
AssemblyEntry Resolve(AssemblyNameReference current)
|
||||
AssemblyEntry? Resolve(string assemblyName)
|
||||
{
|
||||
if (Assembly.Definition.Name.Name == current.Name)
|
||||
if (EntryAssembly.Name == assemblyName)
|
||||
{
|
||||
return Assembly;
|
||||
return EntryAssembly;
|
||||
}
|
||||
|
||||
var referencedAssemblyCandidate = FindCandidate(current, Dependencies);
|
||||
var bclAssemblyCandidate = FindCandidate(current, Bcl);
|
||||
|
||||
// Resolution logic. For right now, we will prefer the mono BCL version of a given
|
||||
// assembly if there is a candidate assembly and an equivalent mono assembly.
|
||||
if (bclAssemblyCandidate != null)
|
||||
if (Bcl.TryGetValue(assemblyName, out var assembly) ||
|
||||
Dependencies.TryGetValue(assemblyName, out assembly))
|
||||
{
|
||||
return bclAssemblyCandidate;
|
||||
}
|
||||
|
||||
return referencedAssemblyCandidate;
|
||||
}
|
||||
|
||||
AssemblyEntry FindCandidate(AssemblyNameReference current, AssemblyEntry[] candidates)
|
||||
{
|
||||
// Do simple name match. Assume no duplicates.
|
||||
foreach (var candidate in candidates)
|
||||
{
|
||||
if (current.Name == candidate.Definition.Name.Name)
|
||||
{
|
||||
return candidate;
|
||||
}
|
||||
return assembly;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static IReadOnlyList<string> GetAssemblyReferences(string assemblyPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var peReader = new PEReader(File.OpenRead(assemblyPath));
|
||||
if (!peReader.HasMetadata)
|
||||
{
|
||||
return Array.Empty<string>(); // not a managed assembly
|
||||
}
|
||||
|
||||
var metadataReader = peReader.GetMetadataReader();
|
||||
|
||||
var references = new List<string>();
|
||||
foreach (var handle in metadataReader.AssemblyReferences)
|
||||
{
|
||||
var reference = metadataReader.GetAssemblyReference(handle);
|
||||
var referenceName = metadataReader.GetString(reference.Name);
|
||||
|
||||
references.Add(referenceName);
|
||||
}
|
||||
|
||||
return references;
|
||||
}
|
||||
catch (BadImageFormatException)
|
||||
{
|
||||
// not a PE file, or invalid metadata
|
||||
}
|
||||
|
||||
return Array.Empty<string>(); // not a managed assembly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{ToString(),nq}")]
|
||||
public class AssemblyEntry
|
||||
public readonly struct AssemblyEntry
|
||||
{
|
||||
public AssemblyEntry(string path, AssemblyDefinition definition)
|
||||
public AssemblyEntry(string path, string name)
|
||||
{
|
||||
Path = path;
|
||||
Definition = definition;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
public AssemblyDefinition Definition { get; set; }
|
||||
public string Path { get; }
|
||||
public string Name { get; }
|
||||
|
||||
public override string ToString() => Definition.FullName;
|
||||
public override string ToString() => Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,12 +27,8 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Condition="'$(BuildNodeJS)' != 'false' and '$(BuildingInsideVisualStudio)' != 'true'" Include="$(RepoRoot)src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj" ReferenceOutputAssembly="false" />
|
||||
<Reference Include="Microsoft.AspNetCore.Components" />
|
||||
<Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
|
||||
<Reference Include="Microsoft.Extensions.FileProviders.Composite" />
|
||||
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
|
||||
<Reference Include="Mono.Cecil" />
|
||||
<Reference Include="System.CodeDom" />
|
||||
<Reference Include="System.Reflection.Metadata" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -623,14 +623,12 @@
|
|||
Inputs="$(BlazorBuildBootJsonInputsCache);@(_BlazorDependencyInput)"
|
||||
Outputs="$(BlazorBootJsonIntermediateOutputPath)">
|
||||
<ItemGroup>
|
||||
<_UnlinkedAppReferencesPaths Include="@(_BlazorDependencyInput)" />
|
||||
<_AppReferences Include="@(BlazorItemOutput->WithMetadataValue('Type','Assembly')->WithMetadataValue('PrimaryOutput','')->'%(FileName)%(Extension)')" />
|
||||
<_AppReferences Include="@(BlazorItemOutput->WithMetadataValue('Type','Assembly')->'%(FileName)%(Extension)')" />
|
||||
<_AppReferences Include="@(BlazorItemOutput->WithMetadataValue('Type','Pdb')->'%(FileName)%(Extension)')" Condition="'$(BlazorEnableDebugging)' == 'true'" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<_LinkerEnabledFlag Condition="'$(_BlazorShouldLinkApplicationAssemblies)' != ''">--linker-enabled</_LinkerEnabledFlag>
|
||||
<_ReferencesArg Condition="'@(_AppReferences)' != ''">--references "$(BlazorBootJsonReferencesFilePath)"</_ReferencesArg>
|
||||
<_EmbeddedResourcesArg Condition="'@(_UnlinkedAppReferencesPaths)' != ''">--embedded-resources "$(BlazorEmbeddedResourcesConfigFilePath)"</_EmbeddedResourcesArg>
|
||||
</PropertyGroup>
|
||||
|
||||
<WriteLinesToFile
|
||||
|
|
@ -638,13 +636,7 @@
|
|||
Lines="@(_AppReferences)"
|
||||
Overwrite="true" />
|
||||
|
||||
<WriteLinesToFile
|
||||
Condition="'@(_UnlinkedAppReferencesPaths)' != ''"
|
||||
File="$(BlazorEmbeddedResourcesConfigFilePath)"
|
||||
Lines="@(_UnlinkedAppReferencesPaths)"
|
||||
Overwrite="true" />
|
||||
|
||||
<Exec Command="$(BlazorBuildExe) write-boot-json "@(IntermediateAssembly)" $(_ReferencesArg) $(_EmbeddedResourcesArg) $(_LinkerEnabledFlag) --output "$(BlazorBootJsonIntermediateOutputPath)"" />
|
||||
<Exec Command="$(BlazorBuildExe) write-boot-json "@(IntermediateAssembly)" $(_ReferencesArg) $(_LinkerEnabledFlag) --output "$(BlazorBootJsonIntermediateOutputPath)"" />
|
||||
|
||||
<ItemGroup Condition="Exists('$(BlazorBootJsonIntermediateOutputPath)')">
|
||||
<_BlazorBootJson Include="$(BlazorBootJsonIntermediateOutputPath)" />
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Build.Test
|
||||
|
|
@ -15,48 +13,16 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
|
|||
public void ProducesJsonReferencingAssemblyAndDependencies()
|
||||
{
|
||||
// Arrange/Act
|
||||
var assemblyReferences = new string[] { "System.Abc.dll", "MyApp.ClassLib.dll", };
|
||||
var assemblyReferences = new string[] { "MyApp.EntryPoint.dll", "System.Abc.dll", "MyApp.ClassLib.dll", };
|
||||
var content = BootJsonWriter.GetBootJsonContent(
|
||||
"MyApp.Entrypoint.dll",
|
||||
"MyNamespace.MyType::MyMethod",
|
||||
assemblyReferences,
|
||||
Enumerable.Empty<EmbeddedResourceInfo>(),
|
||||
linkerEnabled: true);
|
||||
|
||||
// Assert
|
||||
var parsedContent = JsonConvert.DeserializeObject<JObject>(content);
|
||||
Assert.Equal("MyApp.Entrypoint.dll", parsedContent["main"].Value<string>());
|
||||
Assert.Equal("MyNamespace.MyType::MyMethod", parsedContent["entryPoint"].Value<string>());
|
||||
Assert.Equal(assemblyReferences, parsedContent["assemblyReferences"].Values<string>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IncludesReferencesToEmbeddedContent()
|
||||
{
|
||||
// Arrange/Act
|
||||
var embeddedContent = new[]
|
||||
{
|
||||
new EmbeddedResourceInfo(EmbeddedResourceKind.Static, "my/static/file"),
|
||||
new EmbeddedResourceInfo(EmbeddedResourceKind.Css, "css/first.css"),
|
||||
new EmbeddedResourceInfo(EmbeddedResourceKind.JavaScript, "javascript/first.js"),
|
||||
new EmbeddedResourceInfo(EmbeddedResourceKind.Css, "css/second.css"),
|
||||
new EmbeddedResourceInfo(EmbeddedResourceKind.JavaScript, "javascript/second.js"),
|
||||
};
|
||||
var content = BootJsonWriter.GetBootJsonContent(
|
||||
"MyApp.Entrypoint",
|
||||
"MyNamespace.MyType::MyMethod",
|
||||
assemblyReferences: new[] { "Something.dll" },
|
||||
embeddedContent: embeddedContent,
|
||||
linkerEnabled: true);
|
||||
|
||||
// Assert
|
||||
var parsedContent = JsonConvert.DeserializeObject<JObject>(content);
|
||||
Assert.Equal(
|
||||
new[] { "css/first.css", "css/second.css" },
|
||||
parsedContent["cssReferences"].Values<string>());
|
||||
Assert.Equal(
|
||||
new[] { "javascript/first.js", "javascript/second.js" },
|
||||
parsedContent["jsReferences"].Values<string>());
|
||||
Assert.Equal("MyApp.Entrypoint.dll", parsedContent["entryAssembly"].Value<string>());
|
||||
Assert.Equal(assemblyReferences, parsedContent["assemblies"].Values<string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
node_modules/
|
||||
dist/Debug/
|
||||
dist/Release/blazor.webassembly.js
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -2,7 +2,6 @@ import '@dotnet/jsinterop';
|
|||
import './GlobalExports';
|
||||
import * as Environment from './Environment';
|
||||
import { monoPlatform } from './Platform/Mono/MonoPlatform';
|
||||
import { getAssemblyNameFromUrl } from './Platform/Url';
|
||||
import { renderBatch } from './Rendering/Renderer';
|
||||
import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch';
|
||||
import { Pointer } from './Platform/Platform';
|
||||
|
|
@ -39,15 +38,13 @@ async function boot(options?: any): Promise<void> {
|
|||
|
||||
// Fetch the boot JSON file
|
||||
const bootConfig = await fetchBootConfigAsync();
|
||||
const embeddedResourcesPromise = loadEmbeddedResourcesAsync(bootConfig);
|
||||
|
||||
if (!bootConfig.linkerEnabled) {
|
||||
console.info('Blazor is running in dev mode without IL stripping. To make the bundle size significantly smaller, publish the application or see https://go.microsoft.com/fwlink/?linkid=870414');
|
||||
}
|
||||
|
||||
// Determine the URLs of the assemblies we want to load, then begin fetching them all
|
||||
const loadAssemblyUrls = [bootConfig.main]
|
||||
.concat(bootConfig.assemblyReferences)
|
||||
const loadAssemblyUrls = bootConfig.assemblies
|
||||
.map(filename => `_framework/_bin/${filename}`);
|
||||
|
||||
try {
|
||||
|
|
@ -56,12 +53,8 @@ async function boot(options?: any): Promise<void> {
|
|||
throw new Error(`Failed to start platform. Reason: ${ex}`);
|
||||
}
|
||||
|
||||
// Before we start running .NET code, be sure embedded content resources are all loaded
|
||||
await embeddedResourcesPromise;
|
||||
|
||||
// Start up the application
|
||||
const mainAssemblyName = getAssemblyNameFromUrl(bootConfig.main);
|
||||
platform.callEntryPoint(mainAssemblyName);
|
||||
platform.callEntryPoint(bootConfig.entryAssembly);
|
||||
}
|
||||
|
||||
async function fetchBootConfigAsync() {
|
||||
|
|
@ -71,36 +64,10 @@ async function fetchBootConfigAsync() {
|
|||
return bootConfigResponse.json() as Promise<BootJsonData>;
|
||||
}
|
||||
|
||||
function loadEmbeddedResourcesAsync(bootConfig: BootJsonData): Promise<any> {
|
||||
const cssLoadingPromises = bootConfig.cssReferences.map(cssReference => {
|
||||
const linkElement = document.createElement('link');
|
||||
linkElement.rel = 'stylesheet';
|
||||
linkElement.href = cssReference;
|
||||
return loadResourceFromElement(linkElement);
|
||||
});
|
||||
const jsLoadingPromises = bootConfig.jsReferences.map(jsReference => {
|
||||
const scriptElement = document.createElement('script');
|
||||
scriptElement.src = jsReference;
|
||||
return loadResourceFromElement(scriptElement);
|
||||
});
|
||||
return Promise.all(cssLoadingPromises.concat(jsLoadingPromises));
|
||||
}
|
||||
|
||||
function loadResourceFromElement(element: HTMLElement) {
|
||||
return new Promise((resolve, reject) => {
|
||||
element.onload = resolve;
|
||||
element.onerror = reject;
|
||||
document.head!.appendChild(element);
|
||||
});
|
||||
}
|
||||
|
||||
// Keep in sync with BootJsonData in Microsoft.AspNetCore.Blazor.Build
|
||||
interface BootJsonData {
|
||||
main: string;
|
||||
entryPoint: string;
|
||||
assemblyReferences: string[];
|
||||
cssReferences: string[];
|
||||
jsReferences: string[];
|
||||
entryAssembly: string;
|
||||
assemblies: string[];
|
||||
linkerEnabled: boolean;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue