Revert live reloading implementation. See PR for explanation.
This commit is contained in:
parent
707e781e5d
commit
d3dc294d5e
11
Blazor.sln
11
Blazor.sln
|
|
@ -90,8 +90,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.VisualStudio.Blaz
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestContentPackage", "test\testapps\TestContentPackage\TestContentPackage.csproj", "{C57382BC-EE93-49D5-BC40-5C98AF8AA048}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiveReloadTestApp", "test\testapps\LiveReloadTestApp\LiveReloadTestApp.csproj", "{0246AA77-1A27-4A67-874B-6EF6F99E414E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{36A7DEB7-5F88-4BFB-B57E-79EEC9950E25}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Blazor.Performance", "benchmarks\Microsoft.AspNetCore.Blazor.Performance\Microsoft.AspNetCore.Blazor.Performance.csproj", "{50F6820F-D058-4E68-9E15-801F893F514E}"
|
||||
|
|
@ -319,14 +317,6 @@ Global
|
|||
{C57382BC-EE93-49D5-BC40-5C98AF8AA048}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C57382BC-EE93-49D5-BC40-5C98AF8AA048}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C57382BC-EE93-49D5-BC40-5C98AF8AA048}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{0246AA77-1A27-4A67-874B-6EF6F99E414E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0246AA77-1A27-4A67-874B-6EF6F99E414E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0246AA77-1A27-4A67-874B-6EF6F99E414E}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0246AA77-1A27-4A67-874B-6EF6F99E414E}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0246AA77-1A27-4A67-874B-6EF6F99E414E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0246AA77-1A27-4A67-874B-6EF6F99E414E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0246AA77-1A27-4A67-874B-6EF6F99E414E}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0246AA77-1A27-4A67-874B-6EF6F99E414E}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
|
||||
{50F6820F-D058-4E68-9E15-801F893F514E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{50F6820F-D058-4E68-9E15-801F893F514E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{50F6820F-D058-4E68-9E15-801F893F514E}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -375,7 +365,6 @@ Global
|
|||
{43E39257-7DC1-46BD-9BD9-2319A1313D07} = {F563ABB6-85FB-4CFC-B0D2-1D5130E8246D}
|
||||
{9088E4E4-B855-457F-AE9E-D86709A5E1F4} = {F563ABB6-85FB-4CFC-B0D2-1D5130E8246D}
|
||||
{C57382BC-EE93-49D5-BC40-5C98AF8AA048} = {4AE0D35B-D97A-44D0-8392-C9240377DCCE}
|
||||
{0246AA77-1A27-4A67-874B-6EF6F99E414E} = {4AE0D35B-D97A-44D0-8392-C9240377DCCE}
|
||||
{50F6820F-D058-4E68-9E15-801F893F514E} = {36A7DEB7-5F88-4BFB-B57E-79EEC9950E25}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { platform } from './Environment';
|
||||
import { getAssemblyNameFromUrl } from './Platform/DotNet';
|
||||
import { enableLiveReloading } from './LiveReloading';
|
||||
import './Rendering/Renderer';
|
||||
import './Services/Http';
|
||||
import './Services/UriHelper';
|
||||
|
|
@ -37,13 +36,6 @@ async function boot() {
|
|||
|
||||
// Start up the application
|
||||
platform.callEntryPoint(entryPointAssemblyName, entryPointMethod, []);
|
||||
|
||||
// Enable live reloading only if there's a "reload" attribute on the <script> tag.
|
||||
// In production, this should not be the case.
|
||||
const reloadUri = thisScriptElem.getAttribute('reload');
|
||||
if (reloadUri) {
|
||||
enableLiveReloading(reloadUri);
|
||||
}
|
||||
}
|
||||
|
||||
function getRequiredBootScriptAttribute(elem: HTMLScriptElement, attributeName: string): string {
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
const pollIntervalMs = 500;
|
||||
const maxPollDurationMs = 10 * 1000;
|
||||
|
||||
export function enableLiveReloading(endpointUri: string) {
|
||||
new ReloadContext(endpointUri).start();
|
||||
}
|
||||
|
||||
class ReloadContext {
|
||||
private _websocketUri: string;
|
||||
private _didConnect = false;
|
||||
private _pollUntilConnectedThenReload = false;
|
||||
private _stopPollingAtTime: Date | null = null;
|
||||
|
||||
constructor(endpointUri: string) {
|
||||
this._websocketUri = toAbsoluteWebSocketUri(endpointUri);
|
||||
}
|
||||
|
||||
start() {
|
||||
if (typeof WebSocket !== 'undefined') {
|
||||
this._attemptToConnect(/* delay */ 0);
|
||||
} else {
|
||||
console.log('Browser does not support WebSocket, so live reloading will be disabled.');
|
||||
}
|
||||
}
|
||||
|
||||
private _attemptToConnect(delayMs: number) {
|
||||
setTimeout(() => {
|
||||
const source = new WebSocket(this._websocketUri);
|
||||
source.onopen = event => this._onOpen();
|
||||
source.onmessage = event => this._onMessage(event.data);
|
||||
source.onerror = event => this._onError();
|
||||
source.onclose = event => this._onClose();
|
||||
}, delayMs);
|
||||
}
|
||||
|
||||
private _onOpen() {
|
||||
this._didConnect = true;
|
||||
|
||||
if (this._pollUntilConnectedThenReload) {
|
||||
reloadNow();
|
||||
}
|
||||
}
|
||||
|
||||
private _onMessage(data: string) {
|
||||
if (data === 'reload') {
|
||||
reloadNow();
|
||||
}
|
||||
}
|
||||
|
||||
private _onClose() {
|
||||
if (this._didConnect) {
|
||||
// Looks like the server is being recycled (or possibly just shut down, but in the
|
||||
// absence of a graceful shutdown we have no way to tell the difference)
|
||||
// Wait until the server appears to be back, then reload
|
||||
this._pollUntilConnectedThenReload = true;
|
||||
this._stopPollingAtTime = new Date(new Date().valueOf() + maxPollDurationMs);
|
||||
this._attemptToConnect(/* delay */ pollIntervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
private _onError() {
|
||||
if (!this._didConnect) {
|
||||
if (this._pollUntilConnectedThenReload) {
|
||||
if (new Date() < this._stopPollingAtTime!) {
|
||||
// Continue polling
|
||||
this._attemptToConnect(/* delay */ pollIntervalMs);
|
||||
}
|
||||
} else {
|
||||
console.error(`The client app was compiled with live reloading enabled, but could not open `
|
||||
+ ` a WebSocket connection to the server at ${this._websocketUri}\n`
|
||||
+ `To fix this inconsistency, either run the server in development mode, or compile the `
|
||||
+ `client app in Release configuration.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toAbsoluteWebSocketUri(uri: string) {
|
||||
const baseUri = document.baseURI;
|
||||
if (baseUri) {
|
||||
const lastSlashPos = baseUri.lastIndexOf('/');
|
||||
const prefix = baseUri.substr(0, lastSlashPos);
|
||||
uri = prefix + uri;
|
||||
}
|
||||
|
||||
// Scheme must be ws: or wss:
|
||||
return uri.replace(/^http/, 'ws');
|
||||
}
|
||||
|
||||
function reloadNow() {
|
||||
location.reload();
|
||||
}
|
||||
|
|
@ -26,10 +26,6 @@ namespace Microsoft.AspNetCore.Blazor.Build.Cli.Commands
|
|||
"Adds a <link rel=stylesheet> tag with the specified 'href' value",
|
||||
CommandOptionType.MultipleValue);
|
||||
|
||||
var reloadUri = command.Option("--reload-uri",
|
||||
"If specified, enables live reloading and specifies the URI of the notification endpoint.",
|
||||
CommandOptionType.SingleValue);
|
||||
|
||||
var outputPath = command.Option("--output",
|
||||
"Path to the output file",
|
||||
CommandOptionType.SingleValue);
|
||||
|
|
@ -59,7 +55,6 @@ namespace Microsoft.AspNetCore.Blazor.Build.Cli.Commands
|
|||
jsReferences.Values.ToArray(),
|
||||
cssReferences.Values.ToArray(),
|
||||
linkerEnabledFlag.HasValue(),
|
||||
reloadUri.Value(),
|
||||
outputPath.Value());
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
IEnumerable<string> jsReferences,
|
||||
IEnumerable<string> cssReferences,
|
||||
bool linkerEnabled,
|
||||
string reloadUri,
|
||||
string outputPath)
|
||||
{
|
||||
var template = GetTemplate(path);
|
||||
|
|
@ -32,7 +31,7 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
}
|
||||
var assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
|
||||
var entryPoint = GetAssemblyEntryPoint(assemblyPath);
|
||||
var updatedContent = GetIndexHtmlContents(template, assemblyName, entryPoint, assemblyReferences, jsReferences, cssReferences, linkerEnabled, reloadUri);
|
||||
var updatedContent = GetIndexHtmlContents(template, assemblyName, entryPoint, assemblyReferences, jsReferences, cssReferences, linkerEnabled);
|
||||
var normalizedOutputPath = Normalize(outputPath);
|
||||
Console.WriteLine("Writing index to: " + normalizedOutputPath);
|
||||
File.WriteAllText(normalizedOutputPath, updatedContent);
|
||||
|
|
@ -104,8 +103,7 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
IEnumerable<string> assemblyReferences,
|
||||
IEnumerable<string> jsReferences,
|
||||
IEnumerable<string> cssReferences,
|
||||
bool linkerEnabled,
|
||||
string reloadUri)
|
||||
bool linkerEnabled)
|
||||
{
|
||||
var resultBuilder = new StringBuilder();
|
||||
|
||||
|
|
@ -146,8 +144,7 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
assemblyEntryPoint,
|
||||
assemblyReferences,
|
||||
linkerEnabled,
|
||||
tag.Attributes,
|
||||
reloadUri);
|
||||
tag.Attributes);
|
||||
|
||||
// Emit tags to reference any specified JS/CSS files
|
||||
AppendReferenceTags(
|
||||
|
|
@ -209,8 +206,7 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
string assemblyEntryPoint,
|
||||
IEnumerable<string> binFiles,
|
||||
bool linkerEnabled,
|
||||
List<KeyValuePair<string, string>> attributes,
|
||||
string reloadUri)
|
||||
List<KeyValuePair<string, string>> attributes)
|
||||
{
|
||||
var assemblyNameWithExtension = $"{assemblyName}.dll";
|
||||
|
||||
|
|
@ -232,11 +228,6 @@ namespace Microsoft.AspNetCore.Blazor.Build
|
|||
attributesDict.Remove("linker-enabled");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(reloadUri))
|
||||
{
|
||||
attributesDict["reload"] = reloadUri;
|
||||
}
|
||||
|
||||
resultBuilder.Append("<script");
|
||||
foreach (var attributePair in attributesDict)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,16 +4,7 @@
|
|||
<PropertyGroup>
|
||||
<DefaultWebContentItemExcludes>$(DefaultWebContentItemExcludes);wwwroot\**</DefaultWebContentItemExcludes>
|
||||
|
||||
<!-- By default, live reloading is on for debug builds -->
|
||||
<UseBlazorLiveReloading Condition="'$(Configuration)' == 'Debug'">true</UseBlazorLiveReloading>
|
||||
<BlazorLiveReloadUri>/_reload</BlazorLiveReloadUri>
|
||||
|
||||
<!-- We can remove this after updating to newer Razor tooling, where it's enabled by default -->
|
||||
<UseRazorBuildServer>true</UseRazorBuildServer>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- In case you're using 'dotnet watch', enable reloading when editing .cshtml files -->
|
||||
<Watch Include="$(ProjectDir)**\*.cshtml" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
</PropertyGroup>
|
||||
<WriteLinesToFile File="$(BlazorMetadataFilePath)" Lines="$(MSBuildProjectFullPath)" Overwrite="true" Encoding="Unicode"/>
|
||||
<WriteLinesToFile File="$(BlazorMetadataFilePath)" Lines="$(OutDir)$(AssemblyName).dll" Overwrite="false" Encoding="Unicode"/>
|
||||
<WriteLinesToFile File="$(BlazorMetadataFilePath)" Condition="'$(UseBlazorLiveReloading)'=='true'" Lines="reload:$(BlazorLiveReloadUri)" Overwrite="false" Encoding="Unicode"/>
|
||||
<ItemGroup>
|
||||
<ContentWithTargetPath Include="$(BlazorMetadataFilePath)" TargetPath="$(BlazorMetadataFileName)" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
<BlazorWebRootName>wwwroot/</BlazorWebRootName>
|
||||
<BlazorIndexHtmlName>Index.html</BlazorIndexHtmlName>
|
||||
<BlazorOutputIndexHtmlName>$(BlazorIndexHtmlName.ToLowerInvariant())</BlazorOutputIndexHtmlName>
|
||||
<BlazorBuildCompletedSignalPath>$(BaseBlazorDistPath)__blazorBuildCompleted</BlazorBuildCompletedSignalPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@
|
|||
<ItemGroup>
|
||||
<FileWrites Include="@(BlazorItemOutput->'%(TargetOutputPath)')" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<_BlazorDidCopyFilesToOutputDirectory>true</_BlazorDidCopyFilesToOutputDirectory>
|
||||
</PropertyGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_BlazorTrackResolveReferencesDidRun" AfterTargets="ResolveReferences">
|
||||
|
|
@ -50,21 +47,6 @@
|
|||
<Message Importance="$(_BlazorStatisticsReportImportance)" Text="%(_BlazorStatisticsOutput.Identity)" />
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
We only issue the reload notification from here if you're *not* building inside VS.
|
||||
If you are building inside VS, then it's possible you're building an arbitrary collection
|
||||
of projects of which this is just one, and it's important to wait until all projects in
|
||||
the build are completed before reloading, so the notification is instead triggered by the
|
||||
VS extension that can see when a complete build process is finished.
|
||||
-->
|
||||
<Target Name="_BlazorIssueLiveReloadNotification"
|
||||
AfterTargets="Build"
|
||||
Condition="'$(UseBlazorLiveReloading)'=='true' AND '$(_BlazorDidCopyFilesToOutputDirectory)'=='true' AND '$(BuildingInsideVisualStudio)'!='true'">
|
||||
<!-- Touch the signal file to trigger a reload -->
|
||||
<WriteLinesToFile File="$(ProjectDir)$(OutputPath)$(BlazorBuildCompletedSignalPath)" Lines="_" />
|
||||
<Delete Files="$(ProjectDir)$(OutputPath)$(BlazorBuildCompletedSignalPath)" />
|
||||
</Target>
|
||||
|
||||
<!-- Preparing blazor files for output:
|
||||
PrepareBlazorOutputs
|
||||
_PrepareBlazorOutputConfiguration
|
||||
|
|
@ -83,7 +65,6 @@
|
|||
_TouchBlazorApplicationAssemblies
|
||||
_GenerateBlazorIndexHtml
|
||||
_BlazorCopyFilesToOutputDirectory
|
||||
_BlazorIssueLiveReloadNotification
|
||||
|
||||
The process for doing builds goes as follows:
|
||||
Produce a hash file with the Hash SDK task and write that hash to a marker file.
|
||||
|
|
@ -590,7 +571,6 @@
|
|||
<BlazorIndexHtmlInput Include="@(BlazorPackageJsRef->'%(FullPath)')" />
|
||||
<BlazorIndexHtmlInput Include="@(BlazorPackageCssRef->'%(FullPath)')" />
|
||||
<BlazorIndexHtmlInput Include="@(_BlazorLinkingOption)" />
|
||||
<BlazorIndexHtmlInput Include="$(BlazorLiveReloadUri)" Condition="'$(UseBlazorLiveReloading)'=='true'" />
|
||||
</ItemGroup>
|
||||
|
||||
<WriteLinesToFile
|
||||
|
|
@ -617,10 +597,9 @@
|
|||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<_LinkerEnabledFlag Condition="'$(_BlazorShouldLinkApplicationAssemblies)' != ''">--linker-enabled</_LinkerEnabledFlag>
|
||||
<_LiveReloadArg Condition="'$(UseBlazorLiveReloading)' == 'true' AND '$(BlazorLiveReloadUri)' != ''">--reload-uri "$(BlazorLiveReloadUri)"</_LiveReloadArg>
|
||||
</PropertyGroup>
|
||||
|
||||
<Exec Command="$(BlazorBuildExe) build @(IntermediateAssembly) --html-page "$(BlazorIndexHtml)" @(_AppReferences->'--reference "%(Identity)"', ' ') @(_JsReferences->'--js "%(Identity)"', ' ') @(_CssReferences->'--css "%(Identity)"', ' ') $(_LinkerEnabledFlag) $(_LiveReloadArg) --output "$(BlazorIndexHtmlOutputPath)"" />
|
||||
<Exec Command="$(BlazorBuildExe) build @(IntermediateAssembly) --html-page "$(BlazorIndexHtml)" @(_AppReferences->'--reference "%(Identity)"', ' ') @(_JsReferences->'--js "%(Identity)"', ' ') @(_CssReferences->'--css "%(Identity)"', ' ') $(_LinkerEnabledFlag) --output "$(BlazorIndexHtmlOutputPath)"" />
|
||||
|
||||
<ItemGroup Condition="Exists('$(BlazorIndexHtmlOutputPath)')">
|
||||
<_BlazorIndex Include="$(BlazorIndexHtmlOutputPath)" />
|
||||
|
|
|
|||
|
|
@ -64,15 +64,6 @@ namespace Microsoft.AspNetCore.Builder
|
|||
});
|
||||
}
|
||||
|
||||
// Definitely don't open a listener for live reloading in production, even if the
|
||||
// client app was compiled with live reloading enabled
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
// Whether or not live reloading is actually enabled depends on the client config
|
||||
// For release builds, it won't be (by default)
|
||||
applicationBuilder.UseBlazorLiveReloading(config);
|
||||
}
|
||||
|
||||
// Finally, use SPA fallback routing (serve default page for anything else,
|
||||
// excluding /_framework/*)
|
||||
applicationBuilder.MapWhen(IsNotFrameworkDir, childAppBuilder =>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ namespace Microsoft.AspNetCore.Blazor.Server
|
|||
public string SourceMSBuildPath { get; }
|
||||
public string SourceOutputAssemblyPath { get; }
|
||||
public string WebRootPath { get; }
|
||||
public string ReloadUri { get; }
|
||||
public string DistPath
|
||||
=> Path.Combine(Path.GetDirectoryName(SourceOutputAssemblyPath), "dist");
|
||||
|
||||
|
|
@ -42,16 +41,6 @@ namespace Microsoft.AspNetCore.Blazor.Server
|
|||
{
|
||||
WebRootPath = webRootPath;
|
||||
}
|
||||
|
||||
const string reloadMarker = "reload:";
|
||||
var reloadUri = configLines
|
||||
.Where(line => line.StartsWith(reloadMarker, StringComparison.Ordinal))
|
||||
.Select(line => line.Substring(reloadMarker.Length))
|
||||
.FirstOrDefault();
|
||||
if (!string.IsNullOrEmpty(reloadUri))
|
||||
{
|
||||
ReloadUri = reloadUri;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,140 +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 Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Server
|
||||
{
|
||||
internal class LiveReloadingContext
|
||||
{
|
||||
// Keep in sync with $(BlazorBuildCompletedSignalPath) in Blazor.MonoRuntime.props
|
||||
private const string BlazorBuildCompletedSignalFile = "__blazorBuildCompleted";
|
||||
|
||||
// If some external automated process is writing multiple files to wwwroot,
|
||||
// you probably want to wait until they've all been written before reloading.
|
||||
// Pausing by 500 milliseconds is a crude effort - we might need a different
|
||||
// mechanism (e.g., waiting until writes have stopped by 500ms).
|
||||
private const int WebRootUpdateDelayMilliseconds = 500;
|
||||
private static byte[] _reloadMessage = Encoding.UTF8.GetBytes("reload");
|
||||
|
||||
// If we don't hold references to them, then on Linux they get disposed.
|
||||
// This static would leak memory if you called UseBlazorLiveReloading continually
|
||||
// throughout the app lifetime, but the intended usage is just during init.
|
||||
private static readonly List<FileSystemWatcher> _pinnedWatchers = new List<FileSystemWatcher>();
|
||||
|
||||
private readonly object _currentReloadListenerLock = new object();
|
||||
private CancellationTokenSource _currentReloadListener
|
||||
= new CancellationTokenSource();
|
||||
|
||||
public void Attach(IApplicationBuilder applicationBuilder, BlazorConfig config)
|
||||
{
|
||||
CreateFileSystemWatchers(config);
|
||||
AddWebSocketsEndpoint(applicationBuilder, config.ReloadUri);
|
||||
}
|
||||
|
||||
private void AddWebSocketsEndpoint(IApplicationBuilder applicationBuilder, string url)
|
||||
{
|
||||
applicationBuilder.UseWebSockets();
|
||||
applicationBuilder.Use((context, next) =>
|
||||
{
|
||||
if (!string.Equals(context.Request.Path, url, StringComparison.Ordinal))
|
||||
{
|
||||
return next();
|
||||
}
|
||||
|
||||
if (!context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
return context.Response.WriteAsync("This endpoint only accepts WebSockets connections.");
|
||||
}
|
||||
|
||||
return HandleWebSocketRequest(
|
||||
context.WebSockets.AcceptWebSocketAsync(),
|
||||
context.RequestAborted);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task HandleWebSocketRequest(Task<WebSocket> webSocketTask, CancellationToken requestAbortedToken)
|
||||
{
|
||||
var webSocket = await webSocketTask;
|
||||
var reloadToken = _currentReloadListener.Token;
|
||||
|
||||
// Wait until either we get a signal to trigger a reload, or the client disconnects
|
||||
// In either case we're done after that. It's the client's job to reload and start
|
||||
// a new live reloading context.
|
||||
try
|
||||
{
|
||||
var reloadOrRequestAbortedToken = CancellationTokenSource
|
||||
.CreateLinkedTokenSource(reloadToken, requestAbortedToken)
|
||||
.Token;
|
||||
await Task.Delay(-1, reloadOrRequestAbortedToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
if (reloadToken.IsCancellationRequested)
|
||||
{
|
||||
await webSocket.SendAsync(
|
||||
_reloadMessage,
|
||||
WebSocketMessageType.Text,
|
||||
true,
|
||||
requestAbortedToken);
|
||||
await webSocket.CloseAsync(
|
||||
WebSocketCloseStatus.NormalClosure,
|
||||
"Requested reload",
|
||||
requestAbortedToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateFileSystemWatchers(BlazorConfig config)
|
||||
{
|
||||
// Watch for the "build completed" signal in the dist dir
|
||||
var distFileWatcher = new FileSystemWatcher(config.DistPath);
|
||||
distFileWatcher.Deleted += (sender, eventArgs) => {
|
||||
if (eventArgs.Name.Equals(BlazorBuildCompletedSignalFile, StringComparison.Ordinal))
|
||||
{
|
||||
RequestReload(0);
|
||||
}
|
||||
};
|
||||
distFileWatcher.EnableRaisingEvents = true;
|
||||
_pinnedWatchers.Add(distFileWatcher);
|
||||
|
||||
// If there's a WebRootPath, watch for any file modification there
|
||||
// WebRootPath is only used in dev builds, where we want to serve from wwwroot directly
|
||||
// without requiring the developer to rebuild after changing the static files there.
|
||||
// In production there's no need for it.
|
||||
if (!string.IsNullOrEmpty(config.WebRootPath))
|
||||
{
|
||||
var webRootWatcher = new FileSystemWatcher(config.WebRootPath);
|
||||
webRootWatcher.Deleted += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds);
|
||||
webRootWatcher.Created += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds);
|
||||
webRootWatcher.Changed += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds);
|
||||
webRootWatcher.Renamed += (sender, evtArgs) => RequestReload(WebRootUpdateDelayMilliseconds);
|
||||
webRootWatcher.EnableRaisingEvents = true;
|
||||
_pinnedWatchers.Add(webRootWatcher);
|
||||
}
|
||||
}
|
||||
|
||||
private void RequestReload(int delayMilliseconds)
|
||||
{
|
||||
Task.Delay(delayMilliseconds).ContinueWith(_ =>
|
||||
{
|
||||
lock (_currentReloadListenerLock)
|
||||
{
|
||||
// Lock just to be sure two threads don't assign different new CTSs, of which
|
||||
// only one would later get cancelled.
|
||||
_currentReloadListener.Cancel();
|
||||
_currentReloadListener = new CancellationTokenSource();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +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 Microsoft.AspNetCore.Builder;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.Server
|
||||
{
|
||||
internal static class LiveReloadingExtensions
|
||||
{
|
||||
public static void UseBlazorLiveReloading(
|
||||
this IApplicationBuilder applicationBuilder,
|
||||
BlazorConfig config)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(config.ReloadUri))
|
||||
{
|
||||
var context = new LiveReloadingContext();
|
||||
context.Attach(applicationBuilder, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
|
|||
assemblyReferences,
|
||||
jsReferences,
|
||||
cssReferences,
|
||||
linkerEnabled: true,
|
||||
reloadUri: "/my/reload");
|
||||
linkerEnabled: true);
|
||||
|
||||
// Act & Assert: Start and end is not modified (including formatting)
|
||||
Assert.StartsWith(htmlTemplatePrefix, instance);
|
||||
|
|
@ -54,7 +53,6 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
|
|||
Assert.Equal("MyApp.Entrypoint.dll", scriptElem.GetAttribute("main"));
|
||||
Assert.Equal("MyNamespace.MyType::MyMethod", scriptElem.GetAttribute("entrypoint"));
|
||||
Assert.Equal("System.Abc.dll,MyApp.ClassLib.dll", scriptElem.GetAttribute("references"));
|
||||
Assert.Equal("/my/reload", scriptElem.GetAttribute("reload"));
|
||||
Assert.False(scriptElem.HasAttribute("type"));
|
||||
Assert.Equal(string.Empty, scriptElem.Attributes["custom1"].Value);
|
||||
Assert.Equal("value", scriptElem.Attributes["custom2"].Value);
|
||||
|
|
@ -70,30 +68,6 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
|
|||
linkElems.Select(tag => tag.GetAttribute("href")),
|
||||
cssReferences);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OmitsReloadAttributeIfNoReloadUriSpecified()
|
||||
{
|
||||
// Arrange
|
||||
var htmlTemplate = "<script type='blazor-boot'></script>";
|
||||
var assemblyReferences = new string[] { "System.Abc.dll", "MyApp.ClassLib.dll", };
|
||||
|
||||
// Act
|
||||
var fileContents = IndexHtmlWriter.GetIndexHtmlContents(
|
||||
htmlTemplate,
|
||||
"MyApp.Entrypoint",
|
||||
"MyNamespace.MyType::MyMethod",
|
||||
assemblyReferences,
|
||||
/* js references */ new string[] {},
|
||||
/* js references */ new string[] {},
|
||||
/* linkerEnabled */ true,
|
||||
/* reloadUri */ null);
|
||||
|
||||
// Assert
|
||||
var parsedHtml = new HtmlParser().Parse(fileContents);
|
||||
var scriptElem = parsedHtml.QuerySelector("script");
|
||||
Assert.False(scriptElem.HasAttribute("reload"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SuppliesHtmlTemplateUnchangedIfNoBootScriptPresent()
|
||||
|
|
@ -105,7 +79,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
|
|||
var cssReferences = new string[] { "my/styles.css" };
|
||||
|
||||
var content = IndexHtmlWriter.GetIndexHtmlContents(
|
||||
htmlTemplate, "MyApp.Entrypoint", "MyNamespace.MyType::MyMethod", assemblyReferences, jsReferences, cssReferences, linkerEnabled: true, reloadUri: "/my/reload");
|
||||
htmlTemplate, "MyApp.Entrypoint", "MyNamespace.MyType::MyMethod", assemblyReferences, jsReferences, cssReferences, linkerEnabled: true);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(htmlTemplate, content);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
<ProjectReference Include="..\..\samples\StandaloneApp\StandaloneApp.csproj" />
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Blazor.Cli\Microsoft.AspNetCore.Blazor.Cli.csproj" />
|
||||
<ProjectReference Include="..\testapps\BasicTestApp\BasicTestApp.csproj" />
|
||||
<ProjectReference Include="..\testapps\LiveReloadTestApp\LiveReloadTestApp.csproj" />
|
||||
<ProjectReference Include="..\testapps\TestServer\TestServer.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,118 +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 LiveReloadTestApp;
|
||||
using Microsoft.AspNetCore.Blazor.E2ETest.Infrastructure;
|
||||
using Microsoft.AspNetCore.Blazor.E2ETest.Infrastructure.ServerFixtures;
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Support.UI;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Blazor.E2ETest.Tests
|
||||
{
|
||||
// We need an entirely separate test app for the live reloading tests, because
|
||||
// otherwise it might break other tests that were running parallel (e.g., if we
|
||||
// triggered a reload here while other tests were waiting for something to happen).
|
||||
|
||||
public class LiveReloadingTest
|
||||
: ServerTestBase<DevHostServerFixture<LiveReloadTestApp.Program>>
|
||||
{
|
||||
private const string ServerPathBase = "/live/reloading/subdir";
|
||||
private readonly DevHostServerFixture<Program> _serverFixture;
|
||||
|
||||
public LiveReloadingTest(BrowserFixture browserFixture, DevHostServerFixture<Program> serverFixture)
|
||||
: base(browserFixture, serverFixture)
|
||||
{
|
||||
_serverFixture = serverFixture;
|
||||
serverFixture.Environment = "Development"; // Otherwise the server won't accept live reloading connections
|
||||
serverFixture.PathBase = ServerPathBase;
|
||||
Navigate(ServerPathBase);
|
||||
WaitUntilLoaded();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReloadsWhenWebRootFilesAreModified()
|
||||
{
|
||||
// Verify we have the expected starting point
|
||||
var jsFileOutputSelector = By.Id("some-js-file-output");
|
||||
Assert.Equal("initial value", Browser.FindElement(jsFileOutputSelector).Text);
|
||||
|
||||
var jsFilePath = Path.Combine(_serverFixture.ContentRoot, "wwwroot", "someJsFile.js");
|
||||
var origContents = File.ReadAllText(jsFilePath);
|
||||
try
|
||||
{
|
||||
// Edit the source file on disk
|
||||
var newContents = origContents.Replace("'initial value'", "'modified value'");
|
||||
File.WriteAllText(jsFilePath, newContents);
|
||||
|
||||
// See that the page reloads and reflects the updated source file
|
||||
new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until(
|
||||
driver => driver.FindElement(jsFileOutputSelector).Text == "modified value");
|
||||
WaitUntilLoaded();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Restore original state
|
||||
File.WriteAllText(jsFilePath, origContents);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReloadsWhenBlazorAppRebuilds()
|
||||
{
|
||||
// Verify we have the expected starting point
|
||||
var appElementSelector = By.TagName("app");
|
||||
Assert.Equal("Hello, world!", Browser.FindElement(appElementSelector).Text);
|
||||
|
||||
var cshtmlFilePath = Path.Combine(_serverFixture.ContentRoot, "Home.cshtml");
|
||||
var origContents = File.ReadAllText(cshtmlFilePath);
|
||||
try
|
||||
{
|
||||
// Edit the source file on disk
|
||||
var newContents = origContents.Replace("Hello", "Goodbye");
|
||||
File.WriteAllText(cshtmlFilePath, newContents);
|
||||
|
||||
// Trigger build
|
||||
var buildConfiguration = DetectBuildConfiguration(_serverFixture.ContentRoot);
|
||||
var buildProcess = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "dotnet",
|
||||
Arguments = $"build --no-restore --no-dependencies -c {buildConfiguration}",
|
||||
WorkingDirectory = _serverFixture.ContentRoot
|
||||
});
|
||||
Assert.True(buildProcess.WaitForExit(60 * 1000));
|
||||
Assert.Equal(0, buildProcess.ExitCode);
|
||||
|
||||
// See that the page reloads and reflects the updated source file
|
||||
new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until(
|
||||
driver => driver.FindElement(appElementSelector).Text == "Goodbye, world!");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Restore original state
|
||||
File.WriteAllText(cshtmlFilePath, origContents);
|
||||
}
|
||||
}
|
||||
|
||||
private object DetectBuildConfiguration(string contentRoot)
|
||||
{
|
||||
// We want the test to issue the build with the same configuration that
|
||||
// the project was already built with (otherwise there will be errors because
|
||||
// of having multiple directories under /bin, plus it means we don't need
|
||||
// to restore and rebuild all dependencies so it's faster)
|
||||
var binDirInfo = new DirectoryInfo(Path.Combine(contentRoot, "bin"));
|
||||
var configurationDirs = binDirInfo.GetDirectories();
|
||||
Assert.Single(configurationDirs);
|
||||
return configurationDirs[0].Name;
|
||||
}
|
||||
|
||||
private void WaitUntilLoaded()
|
||||
{
|
||||
new WebDriverWait(Browser, TimeSpan.FromSeconds(30)).Until(
|
||||
driver => driver.FindElement(By.TagName("app")).Text != "Loading...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
<h1>Hello, world!</h1>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
|
||||
<!-- Local alternative to <RunArguments>blazor serve</RunArguments> -->
|
||||
<RunCommand>dotnet</RunCommand>
|
||||
<RunArguments>run --project ..\..\..\src\Microsoft.AspNetCore.Blazor.Cli serve --pathbase /live/reloading/subdir</RunArguments>
|
||||
|
||||
<!-- For CI, force live reloading on even though it builds in release mode -->
|
||||
<UseBlazorLiveReloading>true</UseBlazorLiveReloading>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Local alternative to <PackageReference Include="Microsoft.AspNetCore.Blazor.Build" /> -->
|
||||
<Import Project="..\..\..\src\Microsoft.AspNetCore.Blazor.Build\ReferenceFromSource.props" />
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.Blazor.Browser\Microsoft.AspNetCore.Blazor.Browser.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.Blazor\Microsoft.AspNetCore.Blazor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,15 +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 Microsoft.AspNetCore.Blazor.Browser.Rendering;
|
||||
|
||||
namespace LiveReloadTestApp
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
new BrowserRenderer().AddComponent<Home>("app");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Live reload test app</title>
|
||||
<base href="/live/reloading/subdir/" />
|
||||
</head>
|
||||
<body>
|
||||
<app>Loading...</app>
|
||||
<div id="some-js-file-output"></div>
|
||||
<script type="blazor-boot"></script>
|
||||
|
||||
<script>
|
||||
// The test about live reloading should be independent of any
|
||||
// dev server caching headers
|
||||
document.write("<script src='someJsFile.js?nocache=" + (new Date().valueOf()) + "'></" + "script>");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
// We modify this on disk during E2E tests to verify it causes a reload
|
||||
var valueToWrite = 'initial value';
|
||||
document.getElementById('some-js-file-output').textContent = valueToWrite;
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
using Microsoft.VisualStudio.Shell.Interop;
|
||||
|
||||
namespace Microsoft.VisualStudio.BlazorExtension
|
||||
{
|
||||
|
|
@ -12,24 +11,8 @@ namespace Microsoft.VisualStudio.BlazorExtension
|
|||
[PackageRegistration(UseManagedResourcesOnly = true)]
|
||||
[AboutDialogInfo(PackageGuidString, "ASP.NET Core Blazor Language Services", "#110", "112")]
|
||||
[Guid(BlazorPackage.PackageGuidString)]
|
||||
[ProvideAutoLoad(UIContextGuids80.SolutionExists)]
|
||||
public sealed class BlazorPackage : Package
|
||||
{
|
||||
public const string PackageGuidString = "d9fe04bc-57a7-4107-915e-3a5c2f9e19fb";
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
RegisterLiveReloadBuildWatcher();
|
||||
}
|
||||
|
||||
private void RegisterLiveReloadBuildWatcher()
|
||||
{
|
||||
// No need to unadvise, as this only happens once anyway
|
||||
ThreadHelper.ThrowIfNotOnUIThread();
|
||||
var buildManager = (IVsSolutionBuildManager)GetService(typeof(SVsSolutionBuildManager));
|
||||
var hr = buildManager.AdviseUpdateSolutionEvents(new LiveReloadBuildWatcher(), out var cookie);
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,119 +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 Microsoft.VisualStudio.ProjectSystem.Properties;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
using Microsoft.VisualStudio.Shell.Interop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Microsoft.VisualStudio.BlazorExtension
|
||||
{
|
||||
internal class LiveReloadBuildWatcher : IVsUpdateSolutionEvents2
|
||||
{
|
||||
const string BlazorProjectCapability = "Blazor";
|
||||
|
||||
private bool _isListeningForProjectBuilds = false;
|
||||
private List<string> _signalFilePathsToNotify = new List<string>();
|
||||
|
||||
public int UpdateSolution_Begin(ref int pfCancelUpdate)
|
||||
{
|
||||
_signalFilePathsToNotify.Clear();
|
||||
_isListeningForProjectBuilds = true;
|
||||
return VSConstants.S_OK;
|
||||
}
|
||||
|
||||
public int UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand)
|
||||
{
|
||||
_isListeningForProjectBuilds = false;
|
||||
|
||||
foreach (var fullPath in _signalFilePathsToNotify)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllText(fullPath, string.Empty);
|
||||
File.Delete(fullPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AttemptLogError($"Blazor live reloading was unable to write to the signal " +
|
||||
$"file at {fullPath}. To disable live reloading, set the property " +
|
||||
$"'UseBlazorLiveReloading' to 'false' in your project file." +
|
||||
$"\nThe exception was: {ex.Message}\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
return VSConstants.S_OK;
|
||||
}
|
||||
|
||||
private void AttemptLogError(string message)
|
||||
{
|
||||
ThreadHelper.ThrowIfNotOnUIThread();
|
||||
var outputWindow = (IVsOutputWindow)Package.GetGlobalService(typeof(SVsOutputWindow));
|
||||
if (outputWindow != null)
|
||||
{
|
||||
outputWindow.GetPane(VSConstants.OutputWindowPaneGuid.BuildOutputPane_guid, out var pane);
|
||||
if (pane != null)
|
||||
{
|
||||
pane.OutputString(message);
|
||||
pane.Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int UpdateSolution_StartUpdate(ref int pfCancelUpdate)
|
||||
=> VSConstants.S_OK;
|
||||
|
||||
public int UpdateSolution_Cancel()
|
||||
=> VSConstants.S_OK;
|
||||
|
||||
public int OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy)
|
||||
=> VSConstants.S_OK;
|
||||
|
||||
public int UpdateProjectCfg_Begin(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, ref int pfCancel)
|
||||
=> VSConstants.S_OK;
|
||||
|
||||
public int UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel)
|
||||
{
|
||||
if (_isListeningForProjectBuilds
|
||||
&& fSuccess == 1 // i.e., build succeeded
|
||||
&& pHierProj.IsCapabilityMatch(BlazorProjectCapability))
|
||||
{
|
||||
var configuredProject = ((IVsBrowseObjectContext)pCfgProj).ConfiguredProject;
|
||||
var projectLockService = configuredProject
|
||||
.UnconfiguredProject
|
||||
.ProjectService
|
||||
.Services
|
||||
.ProjectLockService;
|
||||
|
||||
ThreadHelper.JoinableTaskFactory.Run(async delegate
|
||||
{
|
||||
using (var access = await projectLockService.ReadLockAsync())
|
||||
{
|
||||
var project = await access.GetProjectAsync(configuredProject);
|
||||
|
||||
// Now we can evaluate MSBuild properties
|
||||
var useLiveReloading = project.GetPropertyValue("UseBlazorLiveReloading");
|
||||
var projectDir = project.GetPropertyValue("ProjectDir");
|
||||
var outputPath = project.GetPropertyValue("OutputPath");
|
||||
var signalFilePath = project.GetPropertyValue("BlazorBuildCompletedSignalPath");
|
||||
if (string.Equals(useLiveReloading, "true", StringComparison.Ordinal)
|
||||
&& !string.IsNullOrEmpty(projectDir)
|
||||
&& !string.IsNullOrEmpty(outputPath)
|
||||
&& !string.IsNullOrEmpty(signalFilePath))
|
||||
{
|
||||
var fullPath = Path.Combine(
|
||||
projectDir,
|
||||
outputPath,
|
||||
signalFilePath);
|
||||
_signalFilePathsToNotify.Add(fullPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return VSConstants.S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
<MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
<UseCodebase>true</UseCodebase>
|
||||
|
||||
<!-- Don't import the directory props and targets, they aren't compatible with an old-style csproj -->
|
||||
<ImportDirectoryBuildProps>false</ImportDirectoryBuildProps>
|
||||
<ImportDirectoryBuildTargets>false</ImportDirectoryBuildTargets>
|
||||
|
|
@ -164,7 +163,6 @@
|
|||
<Visible>false</Visible>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
We need to generate the assembly attributes for our assembly using the version from the build, so
|
||||
we can flow it to the about dialog.
|
||||
|
|
@ -174,49 +172,27 @@
|
|||
<_Parameter1>$(VersionPrefix)-$(VersionSuffix)</_Parameter1>
|
||||
</_VSIXAssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
<Target
|
||||
Name="_GenerateVSIXAssemblyAttributesHash"
|
||||
DependsOnTargets="PrepareForBuild"
|
||||
Condition="'@(_VSIXAssemblyAttribute)' != ''">
|
||||
|
||||
<Target Name="_GenerateVSIXAssemblyAttributesHash" DependsOnTargets="PrepareForBuild" Condition="'@(_VSIXAssemblyAttribute)' != ''">
|
||||
<!-- We only use up to _Parameter1 for most attributes, but other targets may add additional assembly attributes with multiple parameters. -->
|
||||
<Hash ItemsToHash="@(_VSIXAssemblyAttribute->'%(Identity)%(_Parameter1)%(_Parameter2)%(_Parameter3)%(_Parameter4)%(_Parameter5)%(_Parameter6)%(_Parameter7)%(_Parameter8)')">
|
||||
<Output TaskParameter="HashResult" PropertyName="_VSIXAssemblyAttributesHash" />
|
||||
</Hash>
|
||||
|
||||
<WriteLinesToFile
|
||||
Lines="$(_VSIXAssemblyAttributesHash)"
|
||||
File="$(_GeneratedVSIXAssemblyInfoInputsCacheFile)"
|
||||
Overwrite="True"
|
||||
WriteOnlyWhenDifferent="True" />
|
||||
|
||||
<WriteLinesToFile Lines="$(_VSIXAssemblyAttributesHash)" File="$(_GeneratedVSIXAssemblyInfoInputsCacheFile)" Overwrite="True" WriteOnlyWhenDifferent="True" />
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(_GeneratedVSIXAssemblyInfoInputsCacheFile)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="_GenerateVSIXAssemblyAttributes"
|
||||
DependsOnTargets="_GenerateVSIXAssemblyAttributesHash"
|
||||
Inputs="$(_GeneratedVSIXAssemblyInfoInputsCacheFile)"
|
||||
Outputs="$(_GeneratedVSIXAssemblyInfoFile)"
|
||||
BeforeTargets="CoreCompile">
|
||||
|
||||
<Target Name="_GenerateVSIXAssemblyAttributes" DependsOnTargets="_GenerateVSIXAssemblyAttributesHash" Inputs="$(_GeneratedVSIXAssemblyInfoInputsCacheFile)" Outputs="$(_GeneratedVSIXAssemblyInfoFile)" BeforeTargets="CoreCompile">
|
||||
<ItemGroup>
|
||||
<Compile Include="$(_GeneratedVSIXAssemblyInfoFile)">
|
||||
<Visible>false</Visible>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<WriteCodeFragment AssemblyAttributes="@(_VSIXAssemblyAttribute)" Language="C#" OutputFile="$(_GeneratedVSIXAssemblyInfoFile)" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(_GeneratedVSIXAssemblyInfoFile)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
END INTERESTING STUFF
|
||||
|
|
@ -275,7 +251,6 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="AboutDialogInfoAttribute.cs" />
|
||||
<Compile Include="BlazorPackage.cs" />
|
||||
<Compile Include="LiveReloadBuildWatcher.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
@ -334,7 +309,6 @@
|
|||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
|
||||
<!-- Must be defined after the CSharp.targets -->
|
||||
<PropertyGroup>
|
||||
<_GeneratedVSIXAssemblyInfoInputsCacheFile>$(IntermediateOutputPath)$(MSBuildProjectName).VSIXAssemblyInfo.cache.txt</_GeneratedVSIXAssemblyInfoInputsCacheFile>
|
||||
|
|
|
|||
Loading…
Reference in New Issue