Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions src/clients/Wyam/Commands/BuildCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.CommandLine;
using System.IO;
using System.Linq;
using System.Threading;

using Wyam.Common.IO;
using Wyam.Common.Tracing;
using Wyam.Configuration.Preprocessing;
using Trace = Wyam.Common.Tracing.Trace;
using Wyam.LiveReload;

namespace Wyam.Commands
{
Expand All @@ -18,7 +19,7 @@ internal class BuildCommand : Command
private readonly InterlockedBool _exit = new InterlockedBool(false);
private readonly InterlockedBool _newEngine = new InterlockedBool(false);
private readonly ConfigOptions _configOptions = new ConfigOptions();

private bool _preview = false;
private int _previewPort = 5080;
private DirectoryPath _previewVirtualDirectory = null;
Expand All @@ -27,7 +28,7 @@ internal class BuildCommand : Command
private bool _verifyConfig = false;
private DirectoryPath _previewRoot = null;
private bool _watch = false;

public override string Description => "Runs the build process (this is the default command).";

public override string[] SupportedDirectives => new[]
Expand Down Expand Up @@ -106,7 +107,7 @@ private static void AddSettings(IDictionary<string, object> settings, IReadOnlyL
{
foreach (KeyValuePair<string, object> kvp in MetadataParser.Parse(value))
{
settings[kvp.Key] = kvp.Value;
settings[kvp.Key] = kvp.Value;
}
}

Expand All @@ -119,7 +120,7 @@ protected override ExitCode RunCommand(Preprocessor preprocessor)
{
// Get the standard input stream
_configOptions.Stdin = StandardInputReader.Read();

// Fix the root folder and other files
DirectoryPath currentDirectory = Environment.CurrentDirectory;
_configOptions.RootPath = _configOptions.RootPath == null ? currentDirectory : currentDirectory.Combine(_configOptions.RootPath);
Expand Down Expand Up @@ -162,6 +163,15 @@ protected override ExitCode RunCommand(Preprocessor preprocessor)

bool messagePump = false;

// Start the LiveReload server.
bool runLiveReloadServer = _watch;
LiveReloadServer liveReloadServer = null;
if (runLiveReloadServer)
{
liveReloadServer = new LiveReloadServer();
liveReloadServer.StartStandaloneHost();
}

// Start the preview server
IDisposable previewServer = null;
if (_preview)
Expand All @@ -170,7 +180,7 @@ protected override ExitCode RunCommand(Preprocessor preprocessor)
DirectoryPath previewPath = _previewRoot == null
? engineManager.Engine.FileSystem.GetOutputDirectory().Path
: engineManager.Engine.FileSystem.GetOutputDirectory(_previewRoot).Path;
previewServer = PreviewServer.Start(previewPath, _previewPort, _previewForceExtension, _previewVirtualDirectory);
previewServer = PreviewServer.Start(previewPath, _previewPort, _previewForceExtension, _previewVirtualDirectory, liveReloadServer);
}

// Start the watchers
Expand All @@ -192,7 +202,7 @@ protected override ExitCode RunCommand(Preprocessor preprocessor)
{
Trace.Information("Watching configuration file {0}", _configOptions.ConfigFilePath);
configFileWatcher = new ActionFileSystemWatcher(engineManager.Engine.FileSystem.GetOutputDirectory().Path,
new[] { _configOptions.ConfigFilePath.Directory }, false, _configOptions.ConfigFilePath.FileName.FullPath, path =>
new[] {_configOptions.ConfigFilePath.Directory}, false, _configOptions.ConfigFilePath.FileName.FullPath, path =>
{
FilePath filePath = new FilePath(path);
if (_configOptions.ConfigFilePath.Equals(filePath))
Expand Down Expand Up @@ -228,7 +238,7 @@ protected override ExitCode RunCommand(Preprocessor preprocessor)
// Wait for activity
while (true)
{
_messageEvent.WaitOne(); // Blocks the current thread until a signal
_messageEvent.WaitOne(); // Blocks the current thread until a signal
if (_exit)
{
break;
Expand Down Expand Up @@ -283,6 +293,8 @@ protected override ExitCode RunCommand(Preprocessor preprocessor)
{
exitCode = ExitCode.ExecutionError;
}

liveReloadServer?.RebuildCompleted(changedFiles);
}
}

Expand All @@ -301,6 +313,7 @@ protected override ExitCode RunCommand(Preprocessor preprocessor)
inputFolderWatcher?.Dispose();
configFileWatcher?.Dispose();
previewServer?.Dispose();
liveReloadServer?.Dispose();
}

return exitCode;
Expand Down
2 changes: 1 addition & 1 deletion src/clients/Wyam/Commands/PreviewCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ protected override void ParseParameters(ArgumentSyntax syntax)
protected override ExitCode RunCommand(Preprocessor preprocessor)
{
_path = new DirectoryPath(Environment.CurrentDirectory).Combine(_path ?? "output");
using (PreviewServer.Start(_path, _port, _forceExtension, _virtualDirectory))
using (PreviewServer.Start(_path, _port, _forceExtension, _virtualDirectory, null))
{
Trace.Information("Hit any key to exit");
Console.ReadKey();
Expand Down
71 changes: 71 additions & 0 deletions src/clients/Wyam/LiveReload/LiveReloadScriptInjectionMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.IO;
using System.Threading.Tasks;

using AngleSharp;
using AngleSharp.Dom;
using AngleSharp.Dom.Html;
using AngleSharp.Parser.Html;

using Microsoft.Owin;

namespace Wyam.LiveReload
{
internal class LiveReloadScriptInjectionMiddleware : OwinMiddleware
{
private readonly string _scriptPath;

internal HtmlParser HtmlParser { get; set; } = new HtmlParser();

public LiveReloadScriptInjectionMiddleware(OwinMiddleware next, string scriptPath) : base(next)
{
_scriptPath = scriptPath;
}

public override async Task Invoke(IOwinContext context)
{
Stream originalBody = context.Response.Body;
MemoryStream interceptedBody = new MemoryStream();
context.Response.Body = interceptedBody;

await Next.Invoke(context);

if (IsHtmlDocument(context))
{
interceptedBody.Position = 0;
IHtmlDocument document = HtmlParser.Parse(interceptedBody);

IElement script = document.CreateElement("script");
script.SetAttribute("type", "text/javascript");
script.SetAttribute("src", _scriptPath);
document.Body.Append(script);

MemoryStream newContentBuffer = new MemoryStream();
StreamWriter writer = new StreamWriter(newContentBuffer);

document.ToHtml(writer, new AutoSelectedMarkupFormatter());
writer.Flush();

context.Response.ContentLength = newContentBuffer.Length;
newContentBuffer.Position = 0;
newContentBuffer.CopyTo(originalBody);

context.Response.Body = originalBody;
}
else
{
interceptedBody.Position = 0;
interceptedBody.CopyTo(originalBody);

context.Response.Body = originalBody;
}
}

private bool IsHtmlDocument(IOwinContext context)
{
const string rfc2854Type = "text/html";
string contentType = context.Response.ContentType;
return string.Equals(contentType, rfc2854Type, StringComparison.OrdinalIgnoreCase);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;

using Owin;

namespace Wyam.LiveReload
{
public static class LiveReloadScriptInjectionMiddlewareExtensions
{
public static IAppBuilder UseLiveReloadScriptInjections(this IAppBuilder builder, string scriptPath)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
return builder.Use<LiveReloadScriptInjectionMiddleware>(scriptPath);
}
}
}
84 changes: 84 additions & 0 deletions src/clients/Wyam/LiveReload/LiveReloadServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using Microsoft.Owin;
using Microsoft.Owin.FileSystems;
using Microsoft.Owin.StaticFiles;

using Owin;
using Owin.WebSocket.Extensions;

using Wyam.Common.Tracing;
using Wyam.Server;

namespace Wyam.LiveReload
{
internal class LiveReloadServer : IDisposable
{
private readonly ConcurrentBag<IReloadClient> _clients = new ConcurrentBag<IReloadClient>();
private HttpServer _server;

public virtual IEnumerable<IReloadClient> ReloadClients => _clients.ToArray();

public void StartStandaloneHost(int port = 35729, bool throwExceptions = false)
{
try
{
_server = new HttpServer();
_server.StartServer(port, AddHostMiddleware);
}
catch (Exception ex)
{
Trace.Warning($"Error while running the LiveReload server: {ex.Message}");
if (throwExceptions)
{
throw;
}
}

Trace.Verbose($"LiveReload server listening on port {port}.");
}

public void AddInjectionMiddleware(IAppBuilder app)
{
// Inject LR script.
app.UseLiveReloadScriptInjections("/livereload.js");
}

public void AddHostMiddleware(IAppBuilder app)
{
// Host livereload.js
Assembly liveReloadAssembly = typeof(LiveReloadServer).Assembly;
string rootNamespace = typeof(LiveReloadServer).Namespace;
IFileSystem reloadFilesystem = new EmbeddedResourceFileSystem(liveReloadAssembly, $"{rootNamespace}");
app.UseStaticFiles(new StaticFileOptions
{
RequestPath = PathString.Empty,
FileSystem = reloadFilesystem,
ServeUnknownFileTypes = true
});

// Host ws://
app.MapFleckRoute<ReloadClient>("/livereload", connection => _clients.Add((ReloadClient) connection));
}

public void RebuildCompleted(ICollection<string> filesChanged)
{
foreach (IReloadClient client in ReloadClients.Where(x => x.IsConnected))
{
foreach (string modifiedFile in filesChanged)
{
client.NotifyOfChanges(modifiedFile);
}
}
}

public void Dispose()
{
_server?.Dispose();
}
}
}
7 changes: 7 additions & 0 deletions src/clients/Wyam/LiveReload/Messages/BasicMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Wyam.LiveReload.Messages
{
internal class BasicMessage : ILiveReloadMessage
{
public string Command { get; set; }
}
}
13 changes: 13 additions & 0 deletions src/clients/Wyam/LiveReload/Messages/HelloMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Collections.Generic;

namespace Wyam.LiveReload.Messages
{
internal class HelloMessage : ILiveReloadMessage
{
public ICollection<string> Protocols { get; set; }

public string ServerName { get; set; } = "Wyam";

public string Command { get; set; } = "hello";
}
}
7 changes: 7 additions & 0 deletions src/clients/Wyam/LiveReload/Messages/ILiveReloadMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Wyam.LiveReload.Messages
{
internal interface ILiveReloadMessage
{
string Command { get; set; }
}
}
9 changes: 9 additions & 0 deletions src/clients/Wyam/LiveReload/Messages/InfoMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Wyam.LiveReload.Messages
{
internal class InfoMessage : ILiveReloadMessage
{
public string Command { get; set; } = "info";

public string Url { get; set; }
}
}
11 changes: 11 additions & 0 deletions src/clients/Wyam/LiveReload/Messages/ReloadMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Wyam.LiveReload.Messages
{
internal class ReloadMessage : ILiveReloadMessage
{
public string Path { get; set; }

public bool LiveCss { get; set; }

public string Command { get; set; } = "reload";
}
}
Loading